From af76476f09cb02cd7c7601d973b908a8376cbe6e Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 16:25:48 +0800 Subject: [PATCH 01/24] C++ client: build SSL from Tongsuo and pin Thrift to 6dfb0b26 Replace official OpenSSL with Tongsuo 8.4-stable for ASF-compliant bundled libssl/libcrypto. Pin Thrift to commit 6dfb0b26 and update CI, docs, and examples to consume the bundled SSL runtime only. --- .../package-client-cpp-manylinux228.sh | 9 +- .github/workflows/client-cpp-package.yml | 22 +-- .github/workflows/multi-language-client.yml | 25 +-- iotdb-client/client-cpp/CMakeLists.txt | 16 +- iotdb-client/client-cpp/README.md | 58 +++--- iotdb-client/client-cpp/README_zh.md | 8 +- .../client-cpp/cmake/FetchBuildTools.cmake | 2 +- .../client-cpp/cmake/FetchOpenSSL.cmake | 177 +++++++++--------- .../client-cpp/cmake/FetchThrift.cmake | 28 ++- .../client-cpp/examples/CMakeLists.txt | 105 ++++++----- iotdb-client/client-cpp/examples/README.md | 29 ++- iotdb-client/client-cpp/examples/README_zh.md | 24 ++- iotdb-client/client-cpp/pom.xml | 8 +- .../third_party/DEPENDENCIES.md | 4 +- .../package-metadata/third_party/NOTICE | 6 +- iotdb-client/client-cpp/third-party/README.md | 6 +- 16 files changed, 288 insertions(+), 239 deletions(-) diff --git a/.github/scripts/package-client-cpp-manylinux228.sh b/.github/scripts/package-client-cpp-manylinux228.sh index 6bfef0aa415b..a90aaaa75e38 100644 --- a/.github/scripts/package-client-cpp-manylinux228.sh +++ b/.github/scripts/package-client-cpp-manylinux228.sh @@ -73,10 +73,10 @@ java -version # manylinux_2_28 is AlmaLinux 8, whose system OpenSSL is 1.1.1 (EOL and not # Apache-2.0 - must not be bundled/redistributed in an ASF convenience binary). -# Build OpenSSL 3.x from source instead (-Diotdb.openssl.from.source=ON), which -# keeps the glibc 2.28 baseline. OpenSSL 3.x's Configure needs perl plus a few -# modules (IPC::Cmd, Data::Dumper) that are not on the minimal image - install -# them even when perl itself is already present. +# Tongsuo 8.4-stable is always built from source (WITH_SSL=ON), which keeps the +# glibc 2.28 baseline. Tongsuo's Configure needs perl plus a +# few modules (IPC::Cmd, Data::Dumper) that are not on the minimal image - +# install them even when perl itself is already present. if command -v dnf >/dev/null 2>&1; then dnf install -y perl perl-IPC-Cmd perl-Data-Dumper else @@ -86,7 +86,6 @@ fi cd "${GITHUB_WORKSPACE:?GITHUB_WORKSPACE is not set}" ./mvnw clean package -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests \ -Dspotless.skip=true \ - -Diotdb.openssl.from.source=ON \ -Dclient.cpp.package.classifier="${PACKAGE_CLASSIFIER}" SO="iotdb-client/client-cpp/target/install/lib/libiotdb_session.so" diff --git a/.github/workflows/client-cpp-package.yml b/.github/workflows/client-cpp-package.yml index 38eac3fbcbcc..fe5fdc5630d5 100644 --- a/.github/workflows/client-cpp-package.yml +++ b/.github/workflows/client-cpp-package.yml @@ -309,14 +309,12 @@ jobs: shell: bash run: | set -euxo pipefail - # Pin openssl@3 (Apache-2.0): the default 'openssl' formula will move to - # OpenSSL 4.0, which drops the legacy TLS-method APIs Thrift still uses. - brew install boost openssl@3 llvm@17 bison + # Build Tongsuo from source for SSL/TLS (国密 / TLCP support). + brew install boost llvm@17 bison perl ln -sf "$(brew --prefix llvm@17)/bin/clang-format" "$(brew --prefix)/bin/clang-format" echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" echo "$(brew --prefix llvm@17)/bin" >> "$GITHUB_PATH" - # Homebrew OpenSSL is keg-only, so point find_package(OpenSSL) at it. - echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + echo "$(brew --prefix perl)/bin" >> "$GITHUB_PATH" clang-format --version bison --version - name: Cache Maven packages @@ -420,6 +418,7 @@ jobs: shell: pwsh run: | choco install winflexbison3 -y --no-progress + choco install strawberryperl -y --no-progress $boostArgs = @('install', '${{ matrix.boost_choco }}', '-y', '--no-progress') if ('${{ matrix.boost_choco_version }}' -ne '') { $boostArgs += @("--version=${{ matrix.boost_choco_version }}") @@ -433,18 +432,7 @@ jobs: throw "Boost not found under C:\local after installing ${{ matrix.boost_choco }}" } echo $boostDir.FullName >> $env:GITHUB_PATH - # Use a pinned OpenSSL 3.x (Apache-2.0). 'choco install openssl' now - # installs OpenSSL 4.0, which removed the legacy TLS-method APIs that - # Apache Thrift's TSSLSocket still calls. The FireDaemon zip is a clean - # prebuilt OpenSSL 3.5.x that keeps them. - $sslZip = "$env:RUNNER_TEMP\openssl-3.5.3.zip" - $sslDir = "$env:RUNNER_TEMP\openssl-3" - curl.exe -L --fail --retry 3 -o $sslZip 'https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-3.5.3.zip' - Expand-Archive -Path $sslZip -DestinationPath $sslDir -Force - $sslPath = (Get-ChildItem $sslDir -Recurse -Directory -Filter 'x64' | Select-Object -First 1).FullName - if (-not $sslPath) { throw "OpenSSL x64 dir not found under $sslDir" } - echo "$sslPath\bin" >> $env:GITHUB_PATH - echo "OPENSSL_ROOT_DIR=$sslPath" >> $env:GITHUB_ENV + echo "C:\strawberry\perl\bin" >> $env:GITHUB_PATH - name: Cache Maven packages uses: actions/cache@v5 with: diff --git a/.github/workflows/multi-language-client.yml b/.github/workflows/multi-language-client.yml index 5437a6549856..bfcc8326b6f4 100644 --- a/.github/workflows/multi-language-client.yml +++ b/.github/workflows/multi-language-client.yml @@ -124,7 +124,7 @@ jobs: run: | set -euxo pipefail sudo apt-get update - sudo apt-get install -y libboost-all-dev openssl libssl-dev wget + sudo apt-get install -y libboost-all-dev perl wget # jammy (22.04): no clang-format-17 in default repos — use apt.llvm.org (same LLVM 17 as noble/choco/brew) . /etc/os-release if [[ "${VERSION_CODENAME}" == "jammy" ]]; then @@ -144,13 +144,12 @@ jobs: if: runner.os == 'macOS' shell: bash run: | - # Pin openssl@3 (Apache-2.0); the default formula will move to OpenSSL 4.0. - brew install boost openssl@3 llvm@17 bison + # Build Tongsuo from source for SSL/TLS (国密 / TLCP support). + brew install boost llvm@17 bison perl ln -sf "$(brew --prefix llvm@17)/bin/clang-format" "$(brew --prefix)/bin/clang-format" echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" echo "$(brew --prefix llvm@17)/bin" >> "$GITHUB_PATH" - # Homebrew OpenSSL is keg-only, so point find_package(OpenSSL) at it. - echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + echo "$(brew --prefix perl)/bin" >> "$GITHUB_PATH" clang-format --version bison --version sudo rm -rf /Applications/Xcode_14.3.1.app @@ -163,19 +162,10 @@ jobs: run: | choco install winflexbison3 -y choco install boost-msvc-14.3 -y + choco install strawberryperl -y $boost_path = (Get-ChildItem -Path 'C:\local\' -Filter 'boost_*').FullName echo $boost_path >> $env:GITHUB_PATH - - # Pinned OpenSSL 3.x (Apache-2.0): 'choco install openssl' now installs - # OpenSSL 4.0, which removed the legacy TLS-method APIs Thrift uses. - $sslZip = "$env:RUNNER_TEMP\openssl-3.5.3.zip" - $sslDir = "$env:RUNNER_TEMP\openssl-3" - curl.exe -L --fail --retry 3 -o $sslZip 'https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-3.5.3.zip' - Expand-Archive -Path $sslZip -DestinationPath $sslDir -Force - $sslPath = (Get-ChildItem $sslDir -Recurse -Directory -Filter 'x64' | Select-Object -First 1).FullName - if (-not $sslPath) { throw "OpenSSL x64 dir not found under $sslDir" } - echo "$sslPath\bin" >> $env:GITHUB_PATH - echo "OPENSSL_ROOT_DIR=$sslPath" >> $env:GITHUB_ENV + echo "C:\strawberry\perl\bin" >> $env:GITHUB_PATH choco install llvm --version=17.0.6 --force -y clang-format --version - name: Cache Maven packages @@ -198,7 +188,8 @@ jobs: # (was causing problems on windows, but could cause problem on linux, when updating the thrift module) run: | if [[ "${{ matrix.os }}" == "windows-2025-vs2026" ]]; then - ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am -Dcmake.generator="Visual Studio 18 2026" + ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am \ + -Dcmake.generator="Visual Studio 18 2026" else ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am fi diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index ad357dd61a9d..521fe9faf64c 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -78,7 +78,7 @@ if(NOT MSVC) file(WRITE "${_iotdb_cxx11_abi_stamp}" "${_iotdb_cxx11_abi_stamp_value}") endif() -option(WITH_SSL "Build with OpenSSL support" ON) +option(WITH_SSL "Build with Tongsuo SSL/TLS support" ON) option(BUILD_TESTING "Build IT test executables" OFF) option(IOTDB_OFFLINE "Disable all network access during configure" OFF) set(IOTDB_SESSION_VERSION "0.0.0" @@ -97,8 +97,10 @@ else() endif() set(BOOST_VERSION "${_iotdb_default_boost_version}" CACHE STRING "Boost version used when downloading / unpacking (Thrift build only)") -set(THRIFT_VERSION "0.23.0" - CACHE STRING "Apache Thrift version used when downloading / building") +set(THRIFT_GIT_COMMIT "6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242" + CACHE STRING "Apache Thrift git commit used when downloading / building") +set(TONGSUO_GIT_REF "8.4-stable" + CACHE STRING "Tongsuo git ref used when building SSL/TLS from source") if(WIN32) set(IOTDB_OS_DEPS_DIR "${IOTDB_DEPS_DIR}/windows") @@ -145,8 +147,8 @@ if(UNIX AND NOT APPLE) SOVERSION "${IOTDB_SESSION_SOVERSION}") endif() -# When SSL is on we bundle the OpenSSL shared libraries next to libiotdb_session -# in the package lib/ directory. Give the library an $ORIGIN-relative runtime +# When SSL is on we bundle the Tongsuo/OpenSSL-compatible shared libraries next to +# libiotdb_session in the package lib/ directory. Give the library an $ORIGIN-relative runtime # search path so the loader finds them without LD_LIBRARY_PATH / install_name # tweaks, keeping the SDK self-contained. if(WITH_SSL) @@ -240,8 +242,8 @@ install(TARGETS iotdb_session LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) -# Ship the OpenSSL shared libraries we link against next to iotdb_session so the -# packaged SDK is self-contained on machines without a system OpenSSL. +# Ship the Tongsuo shared libraries we link against next to iotdb_session so the +# packaged SDK is self-contained on machines without a system SSL library. if(WITH_SSL) iotdb_install_openssl_runtime() endif() diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index a88293738fdb..b969210dece8 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -300,8 +300,8 @@ so they require glibc 2.28 or newer on the deployment host. | ppc64le | `quay.io/pypa/manylinux_2_28_ppc64le` | | s390x | `quay.io/pypa/manylinux_2_28_s390x` | -Thrift **0.23.0** is compiled from source during the CMake configure step (see -`cmake/FetchThrift.cmake`). Older releases that used pre-built +Thrift commit **`6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242`** (post-0.23.0) is compiled from +source during the CMake configure step (see `cmake/FetchThrift.cmake`). Older releases that used pre-built `iotdb-tools-thrift` Maven artifacts and `-Diotdb-tools-thrift.version=...` for glibc/MSVC compatibility apply only to the **legacy** client-cpp build; with the current CMake build, compatibility is determined by the **compiler @@ -378,15 +378,15 @@ etc. directly. | Option | Default | Purpose | |-----------------------|----------------------------------|----------------------------------------------------------------------------------------------------------| -| `WITH_SSL` | `ON` | Link against OpenSSL and bundle its runtime libraries. See *SSL* below. | +| `WITH_SSL` | `ON` | Link against Tongsuo (OpenSSL-compatible) and bundle its runtime libraries. See *SSL* below. | | `BUILD_TESTING` | `OFF` (Maven sets `ON` for verify) | Build Catch2 IT executables (Catch2 v2.13.7 header downloaded at configure time). | | `CATCH2_INCLUDE_DIR` | (unset) | Pre-downloaded Catch2 include dir (Maven sets this under `target/test/catch2`). | | `IOTDB_OFFLINE` | `OFF` | Disallow any network access during configure. | | `IOTDB_DEPS_DIR` | `/third-party` | Override the local tarball cache directory. | | `BOOST_VERSION` | `1.60.0` (`1.84.0` on macOS) | Boost version that CMake will look for / download. | -| `THRIFT_VERSION` | `0.23.0` | Apache Thrift version to build from source. | +| `THRIFT_GIT_COMMIT` | `6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242` | Apache Thrift git commit to build from source. | +| `TONGSUO_GIT_REF` | `8.4-stable` | Tongsuo git ref built from source when `WITH_SSL=ON`. | | `BOOST_ROOT` | (unset) | Existing Boost install to reuse, equivalent to `-Dboost.include.dir=...` from the legacy build. | -| `OPENSSL_ROOT_DIR` | (unset) | Existing OpenSSL install when `WITH_SSL=ON`. | | `CMAKE_INSTALL_PREFIX`| `/install` | Install location. | | `CMAKE_BUILD_TYPE` | `Release` | Single-config generator build type. Use `Debug` to produce a debug library. | @@ -427,17 +427,17 @@ cmake --build build --config Release --target install | Platform | Required files | |------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| - | `linux/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (and `openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is present) | - | `mac/` | `thrift-0.23.0.tar.gz`, `boost_1_84_0.tar.gz` (newer Boost for Xcode/Clang; Apple ships m4/flex/bison; `openssl-3.5.0.tar.gz` optional) | - | `windows/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz` (Boost headers only - no `b2` build required for `iotdb_session`) | + | `linux/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4-stable.tar.gz` | + | `mac/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_84_0.tar.gz`, `tongsuo-8.4-stable.tar.gz` (Apple ships m4/flex/bison) | + | `windows/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz` (Boost headers only - no `b2` build required for `iotdb_session`) | Reference URLs (the configure step uses the same): - - Apache Thrift 0.23.0: + - Apache Thrift (git): - Boost 1.60.0: - GNU m4 1.4.19: - GNU flex 2.6.4: - GNU bison 3.8: - - OpenSSL 3.5.0: + - Tongsuo 8.4-stable: 2. Run the build with offline mode enabled: @@ -460,8 +460,8 @@ CI environments can share a single cache by setting ### Linux -- Tested with GCC 7+ and Clang 9+. Anything that can compile Apache Thrift - 0.23.0 works. +- Tested with GCC 7+ and Clang 9+. Anything that can compile the pinned Apache + Thrift commit works. - Build deps that must already exist on the host (only required when CMake auto-builds m4/flex/bison from tarball): `make`, `autoconf`, `gcc`, plus the standard C/C++ toolchain. `sudo` is **not** required; @@ -492,11 +492,10 @@ Prerequisites: 2. **flex / bison.** Install and rename `win_flex.exe`→`flex.exe`, `win_bison.exe`→`bison.exe` on `PATH`. -3. **OpenSSL** *(`WITH_SSL=ON` is the default)*: install OpenSSL — e.g. - `choco install openssl`, or a Win64 OpenSSL installer from - — then pass - `-DOPENSSL_ROOT_DIR=...` to CMake if it is not auto-detected. Pass - `-DWITH_SSL=OFF` to build without SSL. +3. **Perl** (for building Tongsuo when `WITH_SSL=ON`). +4. **Tongsuo / SSL** *(`WITH_SSL=ON` is the default)*: Tongsuo 8.4-stable is + always built from source (requires Perl and `nmake` from the VS Developer + Command Prompt). Pass `-DWITH_SSL=OFF` to build without SSL. On Windows the SDK ships as **`iotdb_session.dll`** plus an import library **`iotdb_session.lib`**, built with **`/MD`** (dynamic CRT, same as a @@ -509,27 +508,22 @@ the GNU autotools tarballs assume a POSIX shell environment. ## SSL -`iotdb_session` builds **with OpenSSL by default** (`WITH_SSL=ON`). Disable +`iotdb_session` builds **with SSL/TLS by default** (`WITH_SSL=ON`). Disable it with `-Dwith.ssl=OFF` (Maven) or `-DWITH_SSL=OFF` (standalone CMake). -OpenSSL **3.x** is used (Apache-2.0 licensed). Note that **OpenSSL 4.0 removed** -the legacy TLS-method APIs (`TLSv1_method`, `SSLv3_method`, …) that Apache -Thrift's `TSSLSocket` still calls, so install/point at a 3.x build, not 4.0. - -CMake calls `find_package(OpenSSL)` and uses the system OpenSSL it finds. Its -shared libraries are **bundled into the package `lib/` directory** (next to +[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4-stable** is +**always built from source** during configure (Apache-2.0 licensed, +OpenSSL-compatible API). It adds Chinese commercial cipher and TLCP protocol +support on top of standard TLS. The resulting `libssl` / `libcrypto` shared +libraries are **bundled into the package `lib/` directory** (next to `iotdb_session`, which records an `$ORIGIN`/`@loader_path` runtime path) so the published SDK is self-contained. -Fallbacks: +Host prerequisites when `WITH_SSL=ON`: -- **Linux / macOS** – when no system OpenSSL is found (or - `-DIOTDB_OPENSSL_FROM_SOURCE=ON`, which the Linux packaging build uses so the - AlmaLinux 8 baseline's OpenSSL 1.1.1 is never redistributed), build - `openssl-3.5.0.tar.gz` from source as **shared** libraries and bundle them. -- **Windows** – fail with a friendly message; install a prebuilt OpenSSL 3.x - (e.g. the FireDaemon or slproweb 3.5.x zip) and set `-DOPENSSL_ROOT_DIR=...`. - Building OpenSSL from source via MSVC is out of scope. +- **Linux / macOS** – `perl`, `make`, and a C compiler (Tongsuo `./config`). +- **Windows** – Perl (e.g. Strawberry Perl) and `nmake` from the Visual Studio + Developer Command Prompt. ## Tests diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index 7c4326d661da..30aa89048bee 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -243,10 +243,10 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 | `BOOST_INCLUDEDIR` | `boost.include.dir` | | `CMAKE_BUILD_TYPE` | `cmake.build.type`,例如 `-Dcmake.build.type=Debug` | -SSL 默认开启(`WITH_SSL=ON`)。所捆绑的 Apache Thrift 0.23 同时支持 OpenSSL 1.x -与 3.x,因此直接使用系统的 OpenSSL(任意版本)。CMake 通过 `find_package(OpenSSL)` -解析系统 OpenSSL,找不到时回退到从源码构建 OpenSSL 3.5.0;并会把所用的 OpenSSL -动态库一并复制到产物 `lib/` 目录。Windows 可用 `choco install openssl` 安装。 +SSL 默认开启(`WITH_SSL=ON`)。配置阶段**始终从源码构建** +[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4-stable** +(OpenSSL 兼容 API,Apache-2.0,支持国密/TLCP),并把 `libssl`/`libcrypto` +动态库复制到产物 `lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。 Debug 构建请在配置阶段传入 `-DCMAKE_BUILD_TYPE=Debug`。Windows 使用 Visual Studio 生成器时也需要传入该选项,以便内置 Thrift 静态库使用 Debug MSVC 运行时; diff --git a/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake b/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake index 866cc553954c..7b7589ac5e73 100644 --- a/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake +++ b/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake @@ -266,7 +266,7 @@ if(BISON_EXECUTABLE) if(_bison_ver AND _bison_ver VERSION_LESS _bison_min_version) message(STATUS "[BuildTools] system bison ${_bison_ver} < ${_bison_min_version} " - "(too old for Thrift ${THRIFT_VERSION}); building ${BISON_VERSION} from source") + "(too old for Thrift ${THRIFT_GIT_COMMIT}); building ${BISON_VERSION} from source") unset(BISON_EXECUTABLE CACHE) endif() endif() diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index aaf41b89be41..f044ba75beda 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -18,81 +18,57 @@ # ============================================================================= # FetchOpenSSL.cmake (only included when WITH_SSL=ON) # -# Apache Thrift 0.23 (bundled by this client) builds against OpenSSL 1.x and 3.x, -# so any system OpenSSL is used as-is, whatever its version. -# -# Resolution order: -# 1. find_package(OpenSSL) - any system / vendor install is taken as-is. -# 2. On Linux/macOS, when no system OpenSSL is present: -# use tarball ${IOTDB_OS_DEPS_DIR}/openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz -# or download from openssl.org when not in offline mode, then -# ./config && make && make install_sw into ${CMAKE_BINARY_DIR}/_deps/openssl. -# 3. On Windows: emit a FATAL_ERROR asking for a prebuilt OpenSSL; building -# OpenSSL from source on MSVC is out of scope. +# Builds Tongsuo (OpenSSL-compatible, Apache-2.0) from source for Thrift +# TSSLSocket and iotdb_session. Tongsuo adds Chinese commercial cipher / TLCP +# support on top of the standard TLS stack. # # Side effects: -# Defines imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package -# so callers can just link against them. +# Sets OPENSSL_ROOT_DIR to the local Tongsuo install tree, then defines +# imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package so callers +# can link against them unchanged. # ============================================================================= -# Version built from source when no system OpenSSL is found. Named distinctly -# from find_package's OPENSSL_VERSION output variable to avoid collisions. -set(OPENSSL_FALLBACK_VERSION "3.5.0" - CACHE STRING "OpenSSL version built from source when no system OpenSSL is found") - -# Build OpenSSL from source even if a system one exists. Used by the Linux -# packaging build, whose AlmaLinux 8 baseline ships OpenSSL 1.1.1 (EOL, not -# Apache-2.0, must not be redistributed) - we build 3.x there instead. -option(IOTDB_OPENSSL_FROM_SOURCE - "Ignore any system OpenSSL and build OpenSSL ${OPENSSL_FALLBACK_VERSION} from source" OFF) - -if(NOT IOTDB_OPENSSL_FROM_SOURCE) - find_package(OpenSSL QUIET) - if(OpenSSL_FOUND) - message(STATUS "[OpenSSL] using system OpenSSL ${OPENSSL_VERSION}") - return() - endif() -endif() - -if(WIN32) - message(FATAL_ERROR - "[OpenSSL] WITH_SSL=ON but no OpenSSL was found on Windows. " - "Please install a prebuilt OpenSSL (e.g. 'choco install openssl'), " - "then re-run the configure step with -DOPENSSL_ROOT_DIR=. " - "Pass -DWITH_SSL=OFF to build without SSL.") +# --- Build Tongsuo ${TONGSUO_GIT_REF} from source --- +if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]{7,40}$") + set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") + set(_tongsuo_url "https://github.com/Tongsuo-Project/Tongsuo/archive/${TONGSUO_GIT_REF}.tar.gz") +else() + set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") + set(_tongsuo_url + "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/heads/${TONGSUO_GIT_REF}.tar.gz") endif() -# --- Linux / macOS: build OpenSSL ${OPENSSL_FALLBACK_VERSION} from source - -set(_ossl_tarname "openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz") -set(_ossl_tarball "${IOTDB_OS_DEPS_DIR}/${_ossl_tarname}") +set(_tongsuo_tarname "tongsuo-${TONGSUO_GIT_REF}.tar.gz") +set(_tongsuo_tarball "${IOTDB_OS_DEPS_DIR}/${_tongsuo_tarname}") -if(NOT EXISTS "${_ossl_tarball}") +if(NOT EXISTS "${_tongsuo_tarball}") if(IOTDB_OFFLINE) message(FATAL_ERROR - "[OpenSSL] IOTDB_OFFLINE=ON but ${_ossl_tarname} is missing in ${IOTDB_OS_DEPS_DIR}.") + "[Tongsuo] IOTDB_OFFLINE=ON but ${_tongsuo_tarname} is missing in ${IOTDB_OS_DEPS_DIR}.") endif() - set(_ossl_url "https://www.openssl.org/source/${_ossl_tarname}") - message(STATUS "[OpenSSL] downloading ${_ossl_url}") - file(DOWNLOAD "${_ossl_url}" "${_ossl_tarball}" - SHOW_PROGRESS TLS_VERIFY ON STATUS _st) + message(STATUS "[Tongsuo] downloading ${_tongsuo_url}") + file(DOWNLOAD "${_tongsuo_url}" "${_tongsuo_tarball}" + SHOW_PROGRESS TLS_VERIFY ON + TIMEOUT 600 + STATUS _st) list(GET _st 0 _code) if(NOT _code EQUAL 0) list(GET _st 1 _msg) - file(REMOVE "${_ossl_tarball}") - message(FATAL_ERROR "[OpenSSL] download failed: ${_msg}") + file(REMOVE "${_tongsuo_tarball}") + message(FATAL_ERROR "[Tongsuo] download failed: ${_msg}") endif() endif() -set(_ossl_root "${CMAKE_BINARY_DIR}/_deps/openssl") -set(_ossl_src "${_ossl_root}/src/openssl-${OPENSSL_FALLBACK_VERSION}") -set(_ossl_inst "${_ossl_root}/install") -set(_ossl_stamp "${_ossl_root}/.built-${OPENSSL_FALLBACK_VERSION}") +set(_tongsuo_root "${CMAKE_BINARY_DIR}/_deps/tongsuo") +set(_tongsuo_src "${_tongsuo_root}/src/${_tongsuo_extracted_dir}") +set(_tongsuo_inst "${_tongsuo_root}/install") +set(_tongsuo_stamp "${_tongsuo_root}/.built-${TONGSUO_GIT_REF}") -if(NOT EXISTS "${_ossl_stamp}") - file(REMOVE_RECURSE "${_ossl_root}/src") - file(MAKE_DIRECTORY "${_ossl_root}/src") - message(STATUS "[OpenSSL] extracting ${_ossl_tarball}") - file(ARCHIVE_EXTRACT INPUT "${_ossl_tarball}" DESTINATION "${_ossl_root}/src") +if(NOT EXISTS "${_tongsuo_stamp}") + file(REMOVE_RECURSE "${_tongsuo_root}/src") + file(MAKE_DIRECTORY "${_tongsuo_root}/src") + message(STATUS "[Tongsuo] extracting ${_tongsuo_tarball}") + file(ARCHIVE_EXTRACT INPUT "${_tongsuo_tarball}" DESTINATION "${_tongsuo_root}/src") include(ProcessorCount) ProcessorCount(_jobs) @@ -100,38 +76,67 @@ if(NOT EXISTS "${_ossl_stamp}") set(_jobs 1) endif() - message(STATUS "[OpenSSL] configuring -> ${_ossl_inst}") - # ./config auto-detects the platform target. Build SHARED libraries - # (libssl.so.3 / libcrypto.so.3) so they can be bundled next to - # libiotdb_session and shipped as the SDK's OpenSSL runtime. - execute_process( - COMMAND ./config --prefix=${_ossl_inst} --openssldir=${_ossl_inst}/ssl shared - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] config failed (rc=${_rc})") - endif() + if(WIN32) + find_program(PERL_EXECUTABLE perl REQUIRED) + set(_tongsuo_target "VC-WIN64A") + message(STATUS "[Tongsuo] configuring (${_tongsuo_target}) -> ${_tongsuo_inst}") + execute_process( + COMMAND "${PERL_EXECUTABLE}" Configure enable-ntls no-asm ${_tongsuo_target} + --prefix=${_tongsuo_inst} + --openssldir=${_tongsuo_inst}/ssl + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] Configure failed (rc=${_rc})") + endif() - message(STATUS "[OpenSSL] building (-j${_jobs})") - execute_process( - COMMAND make -j${_jobs} - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] make failed (rc=${_rc})") - endif() + message(STATUS "[Tongsuo] building") + execute_process( + COMMAND nmake + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] nmake failed (rc=${_rc})") + endif() + + execute_process( + COMMAND nmake install_sw + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] nmake install_sw failed (rc=${_rc})") + endif() + else() + message(STATUS "[Tongsuo] configuring -> ${_tongsuo_inst}") + execute_process( + COMMAND ./config --prefix=${_tongsuo_inst} --openssldir=${_tongsuo_inst}/ssl shared enable-ntls + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] config failed (rc=${_rc})") + endif() + + message(STATUS "[Tongsuo] building (-j${_jobs})") + execute_process( + COMMAND make -j${_jobs} + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] make failed (rc=${_rc})") + endif() - execute_process( - COMMAND make install_sw - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] make install_sw failed (rc=${_rc})") + execute_process( + COMMAND make install_sw + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] make install_sw failed (rc=${_rc})") + endif() endif() - file(TOUCH "${_ossl_stamp}") + file(TOUCH "${_tongsuo_stamp}") endif() -set(OPENSSL_ROOT_DIR "${_ossl_inst}" CACHE PATH "OpenSSL root" FORCE) +set(OPENSSL_ROOT_DIR "${_tongsuo_inst}" CACHE PATH "Tongsuo install root" FORCE) set(OPENSSL_USE_STATIC_LIBS OFF) find_package(OpenSSL REQUIRED) -message(STATUS "[OpenSSL] built locally (shared) at ${OPENSSL_ROOT_DIR}") +message(STATUS "[Tongsuo] built from source (shared) at ${OPENSSL_ROOT_DIR}") diff --git a/iotdb-client/client-cpp/cmake/FetchThrift.cmake b/iotdb-client/client-cpp/cmake/FetchThrift.cmake index d69b2a47ad9e..cc611b340134 100644 --- a/iotdb-client/client-cpp/cmake/FetchThrift.cmake +++ b/iotdb-client/client-cpp/cmake/FetchThrift.cmake @@ -41,7 +41,7 @@ include(ExternalProject) -set(_thrift_dirname "thrift-${THRIFT_VERSION}") +set(_thrift_dirname "thrift-${THRIFT_GIT_COMMIT}") set(_thrift_tarname "${_thrift_dirname}.tar.gz") # --------------------------------------------------------------------------- @@ -54,10 +54,13 @@ if(NOT EXISTS "${_thrift_tarball}") "[Thrift] IOTDB_OFFLINE=ON but ${_thrift_tarname} is missing in " "${IOTDB_OS_DEPS_DIR}.") endif() - set(_thrift_url "https://archive.apache.org/dist/thrift/${THRIFT_VERSION}/${_thrift_tarname}") + set(_thrift_url + "https://github.com/apache/thrift/archive/${THRIFT_GIT_COMMIT}.tar.gz") message(STATUS "[Thrift] downloading ${_thrift_url}") file(DOWNLOAD "${_thrift_url}" "${_thrift_tarball}" - SHOW_PROGRESS TLS_VERIFY ON STATUS _thrift_dl) + SHOW_PROGRESS TLS_VERIFY ON + TIMEOUT 600 + STATUS _thrift_dl) list(GET _thrift_dl 0 _code) if(NOT _code EQUAL 0) list(GET _thrift_dl 1 _msg) @@ -73,7 +76,7 @@ set(_thrift_root "${CMAKE_BINARY_DIR}/_deps/thrift") set(_thrift_src "${_thrift_root}/src/${_thrift_dirname}") set(_thrift_build "${_thrift_root}/build") set(_thrift_install "${_thrift_root}/install") -set(_thrift_marker "${_thrift_root}/.extracted-${THRIFT_VERSION}") +set(_thrift_marker "${_thrift_root}/.extracted-${THRIFT_GIT_COMMIT}") set(_thrift_build_config "Release") if(MSVC AND CMAKE_BUILD_TYPE) @@ -89,6 +92,19 @@ if(NOT EXISTS "${_thrift_marker}") file(TOUCH "${_thrift_marker}") endif() +# GitHub archives use thrift-, release tarballs use thrift-. +if(NOT EXISTS "${_thrift_src}/CMakeLists.txt") + file(GLOB _thrift_extracted "${_thrift_root}/src/thrift-*") + list(LENGTH _thrift_extracted _thrift_extracted_count) + if(_thrift_extracted_count EQUAL 1) + list(GET _thrift_extracted 0 _thrift_found) + if(NOT _thrift_found STREQUAL _thrift_src) + message(STATUS "[Thrift] normalizing extracted dir ${_thrift_found} -> ${_thrift_src}") + file(RENAME "${_thrift_found}" "${_thrift_src}") + endif() + endif() +endif() + if(NOT EXISTS "${_thrift_src}/CMakeLists.txt") message(FATAL_ERROR "[Thrift] could not find ${_thrift_src}/CMakeLists.txt after " @@ -138,7 +154,7 @@ endif() if(WITH_SSL) list(APPEND _thrift_cmake_args "-DWITH_OPENSSL=ON") - # Build Thrift's TSSLSocket against the same OpenSSL that iotdb_session links + # Build Thrift's TSSLSocket against the same SSL library that iotdb_session links # and bundles, so the runtime libraries match. find_package does not set # OPENSSL_ROOT_DIR itself, so derive it from the resolved include dir. if(OPENSSL_ROOT_DIR) @@ -169,7 +185,7 @@ if(WITH_SSL) else() set(_thrift_ssl_stamp "-nossl") endif() -set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_VERSION}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}") +set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_GIT_COMMIT}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}") if(NOT EXISTS "${_thrift_stamp}") file(MAKE_DIRECTORY "${_thrift_build}") message(STATUS "[Thrift] configuring ${_thrift_dirname}") diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index 4184199847f8..ed80e15b8c75 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -61,21 +61,55 @@ else() INCLUDE_DIRECTORIES("${IOTDB_SDK_ROOT}/include") endif() -option(WITH_SSL "Build with SSL support" OFF) - -IF(WITH_SSL) - FIND_PACKAGE(OpenSSL REQUIRED) - IF(OpenSSL_FOUND) - MESSAGE(STATUS "OpenSSL found: ${OPENSSL_VERSION}") - INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) - ADD_DEFINITIONS(-DWITH_SSL=1) - ELSE() - MESSAGE(FATAL_ERROR "OpenSSL not found, but WITH_SSL is enabled") - ENDIF() -ELSE() - MESSAGE(STATUS "Building without SSL support") - ADD_DEFINITIONS(-DWITH_SSL=0) -ENDIF() +# Match the SDK default (WITH_SSL=ON in the main client build). When this +# directory is added via add_subdirectory(), the parent cache value wins. +option(WITH_SSL "Build with SSL/TLS support" ON) + +set(_iotdb_use_bundled_ssl OFF) +set(_iotdb_ssl_link_libs "") + +if(NOT _iotdb_examples_in_tree) + file(GLOB _iotdb_bundled_ssl_runtime + "${IOTDB_SDK_ROOT}/lib/libssl*.so*" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.so*" + "${IOTDB_SDK_ROOT}/lib/libssl*.dylib" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.dylib" + "${IOTDB_SDK_ROOT}/lib/libssl*.dll" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.dll") + if(_iotdb_bundled_ssl_runtime) + set(_iotdb_use_bundled_ssl ON) + set(WITH_SSL ON CACHE BOOL "Build with SSL/TLS support" FORCE) + message(STATUS "Using bundled Tongsuo/OpenSSL-compatible libraries from ${IOTDB_SDK_ROOT}/lib") + endif() +endif() + +if(WITH_SSL) + if(_iotdb_examples_in_tree) + add_compile_definitions(WITH_SSL=1) + elseif(_iotdb_use_bundled_ssl) + add_compile_definitions(WITH_SSL=1) + if(UNIX) + find_library(_iotdb_ssl_lib NAMES ssl libssl + PATHS "${IOTDB_SDK_ROOT}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_library(_iotdb_crypto_lib NAMES crypto libcrypto + PATHS "${IOTDB_SDK_ROOT}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + if(_iotdb_ssl_lib AND _iotdb_crypto_lib) + set(_iotdb_ssl_link_libs "${_iotdb_ssl_lib}" "${_iotdb_crypto_lib}") + else() + message(FATAL_ERROR + "Bundled libssl/libcrypto not found under ${IOTDB_SDK_ROOT}/lib") + endif() + endif() + else() + message(FATAL_ERROR + "WITH_SSL=ON requires building inside the IoTDB client tree, or an SDK " + "that bundles libssl/libcrypto under ${IOTDB_SDK_ROOT}/lib. " + "Pass -DWITH_SSL=OFF only for SDKs built without SSL.") + endif() +else() + message(STATUS "Building without SSL support") + add_compile_definitions(WITH_SSL=0) +endif() if(NOT _iotdb_examples_in_tree) find_package(iotdb-session CONFIG QUIET @@ -118,35 +152,19 @@ set(_example_targets tree_example table_example) -# OpenSSL runtime libraries bundled in the SDK lib/ (libssl / libcrypto). When -# building against an unpacked package, copy them next to each example binary so -# the examples run without a system OpenSSL - libiotdb_session records them as -# NEEDED and resolves them via its $ORIGIN runtime path. -set(_iotdb_sdk_ssl_runtime "") -if(NOT _iotdb_examples_in_tree) - file(GLOB _iotdb_sdk_ssl_runtime - "${IOTDB_SDK_ROOT}/lib/libssl*.so*" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.so*" - "${IOTDB_SDK_ROOT}/lib/libssl*.dylib" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.dylib" - "${IOTDB_SDK_ROOT}/lib/libssl*.dll" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.dll") -endif() - foreach(_t IN LISTS _example_targets) - IF(WITH_SSL) - TARGET_LINK_LIBRARIES(${_t} PRIVATE "${_iotdb_link_lib}" OpenSSL::SSL OpenSSL::Crypto) - ELSE() - TARGET_LINK_LIBRARIES(${_t} PRIVATE "${_iotdb_link_lib}") - ENDIF() + if(WITH_SSL AND _iotdb_ssl_link_libs) + target_link_libraries(${_t} PRIVATE "${_iotdb_link_lib}" ${_iotdb_ssl_link_libs}) + else() + target_link_libraries(${_t} PRIVATE "${_iotdb_link_lib}") + endif() IF(UNIX) TARGET_LINK_LIBRARIES(${_t} PRIVATE pthread) ENDIF() - # The packaged libiotdb_session records the bundled OpenSSL libs as DT_NEEDED; - # point the linker at the SDK lib/ so it can resolve them without a system - # OpenSSL present. - if(UNIX AND NOT _iotdb_examples_in_tree) + # The packaged libiotdb_session records the bundled SSL libs as DT_NEEDED; point + # the linker at the SDK lib/ so it can resolve them without a system install. + if(UNIX AND NOT _iotdb_examples_in_tree AND _iotdb_use_bundled_ssl) target_link_directories(${_t} PRIVATE "${IOTDB_SDK_ROOT}/lib") endif() @@ -167,11 +185,11 @@ foreach(_t IN LISTS _example_targets) COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_iotdb_runtime}" $ COMMENT "Copy IoTDB runtime library next to ${_t}") - foreach(_ssl_lib IN LISTS _iotdb_sdk_ssl_runtime) + foreach(_ssl_lib IN LISTS _iotdb_bundled_ssl_runtime) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_ssl_lib}" $ - COMMENT "Copy bundled OpenSSL runtime next to ${_t}") + COMMENT "Copy bundled SSL runtime next to ${_t}") endforeach() elseif(WIN32) message(WARNING "Missing ${_iotdb_runtime}; copy iotdb_session.dll manually before running ${_t}.") @@ -194,9 +212,8 @@ if(EXISTS "${_iotdb_runtime}") COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_iotdb_runtime}" "${_example_dist_dir}/") endif() -# Stage the bundled OpenSSL runtime too, so a copied dist/ runs on a machine -# without a system OpenSSL. -foreach(_ssl_lib IN LISTS _iotdb_sdk_ssl_runtime) +# Stage the bundled SSL runtime too, so a copied dist/ runs without a system SSL. +foreach(_ssl_lib IN LISTS _iotdb_bundled_ssl_runtime) add_custom_command(TARGET example-dist POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_ssl_lib}" "${_example_dist_dir}/") diff --git a/iotdb-client/client-cpp/examples/README.md b/iotdb-client/client-cpp/examples/README.md index 763ec693bee2..d86f6b6202b5 100644 --- a/iotdb-client/client-cpp/examples/README.md +++ b/iotdb-client/client-cpp/examples/README.md @@ -61,8 +61,9 @@ pre-built Thrift workflow only. Linux release packages are built in the ## SDK layout (after unpack) -The SDK zip produced by `client-cpp` contains **public headers only** and one -shared library: +The SDK zip produced by `client-cpp` contains **public headers**, the +`iotdb_session` shared library, and (when built with SSL, the default) +**bundled Tongsuo** runtime libraries (`libssl` / `libcrypto`): ``` client/ @@ -73,7 +74,9 @@ client/ └── lib/ ├── iotdb_session.dll + iotdb_session.lib (Windows) ├── libiotdb_session.so (Linux) - └── libiotdb_session.dylib (macOS) + ├── libiotdb_session.dylib (macOS) + ├── libssl-3-x64.dll + libcrypto-3-x64.dll (Windows SSL runtime, when WITH_SSL=ON) + └── libssl.so* + libcrypto.so* (Linux/macOS SSL runtime, when WITH_SSL=ON) ``` ## Build the examples @@ -106,6 +109,10 @@ cmake -S iotdb-client/client-cpp/examples -B build \ cmake --build build ``` +When the SDK bundles `libssl` / `libcrypto` under `lib/` (default `WITH_SSL=ON` +builds), CMake detects them automatically. A system OpenSSL install is not used. +Pass `-DWITH_SSL=OFF` only for SDKs built without SSL. + Windows (Visual Studio generator): ```powershell @@ -122,6 +129,7 @@ Optional staging folder for deployment: ```bash cmake --build build --target example-dist # -> build/dist/ contains all example binaries + libiotdb_session.{so,dll,dylib} +# and bundled libssl/libcrypto when WITH_SSL=ON ``` ## Run on a clean machine (no compiler, no IoTDB SDK headers) @@ -142,9 +150,12 @@ Copy either from `build/.../Release/` (Windows) / `build/` (Ninja/Make) or from ``` SessionExample.exe iotdb_session.dll +libssl-3-x64.dll +libcrypto-3-x64.dll ``` -(Repeat for the other example names if needed.) +(Repeat for the other example names if needed. Exact SSL DLL names follow the +Tongsuo major version bundled in your SDK zip.) **Prerequisites on the target PC** @@ -164,8 +175,9 @@ iotdb_session.dll If you see “The code execution cannot proceed because VCRUNRuntime140.dll was missing”, install the VC++ redistributable above. -You do **not** need a separate Thrift or Boost runtime; they are inside -`iotdb_session.dll`. +You do **not** need a separate Thrift, Boost, or system OpenSSL runtime; Thrift +and Boost are inside `iotdb_session.dll`, and SSL is provided by the bundled +Tongsuo libraries copied above. ### Linux @@ -174,9 +186,14 @@ You do **not** need a separate Thrift or Boost runtime; they are inside ``` SessionExample libiotdb_session.so +libssl.so* +libcrypto.so* chmod +x SessionExample ``` +Copy the `libssl` / `libcrypto` soname files that ship next to +`libiotdb_session.so` in the SDK `lib/` directory (Tongsuo, OpenSSL-compatible). + **Prerequisites on the target machine** - **glibc** on the target must be **≥ the glibc version on the machine that diff --git a/iotdb-client/client-cpp/examples/README_zh.md b/iotdb-client/client-cpp/examples/README_zh.md index 4adc38a3fc73..b70fa7107c41 100644 --- a/iotdb-client/client-cpp/examples/README_zh.md +++ b/iotdb-client/client-cpp/examples/README_zh.md @@ -59,7 +59,8 @@ Linux 发版包在 `manylinux_2_28` 容器中构建,部署机需要 glibc 2.28 ## SDK 目录结构(解压后) -`client-cpp` 打出的 SDK 压缩包只包含 **公开头文件** 和 **一个共享库**: +`client-cpp` 打出的 SDK 压缩包包含 **公开头文件**、`iotdb_session` 共享库, +以及(默认开启 SSL 时)**内置的 Tongsuo** 运行时(`libssl` / `libcrypto`): ``` client/ @@ -70,7 +71,9 @@ client/ └── lib/ ├── iotdb_session.dll + iotdb_session.lib (Windows) ├── libiotdb_session.so (Linux) - └── libiotdb_session.dylib (macOS) + ├── libiotdb_session.dylib (macOS) + ├── libssl-3-x64.dll + libcrypto-3-x64.dll (Windows SSL 运行时,WITH_SSL=ON) + └── libssl.so* + libcrypto.so* (Linux/macOS SSL 运行时,WITH_SSL=ON) ``` ## 编译示例 @@ -103,6 +106,10 @@ cmake -S iotdb-client/client-cpp/examples -B build \ cmake --build build ``` +若 SDK 的 `lib/` 下已包含 `libssl` / `libcrypto`(默认 `WITH_SSL=ON` 构建), +CMake 会自动检测并链接这些内置库,不会使用系统 OpenSSL。仅当使用未启用 SSL 的 +SDK 时才需要传入 `-DWITH_SSL=OFF`。 + Windows(Visual Studio 生成器): ```powershell @@ -119,6 +126,7 @@ cmake --build build --config Release ```bash cmake --build build --target example-dist # 生成 build/dist/,内含全部示例二进制 + libiotdb_session.{so,dll,dylib} +# 以及 WITH_SSL=ON 时的 libssl/libcrypto ``` ## 在「干净机器」上运行(无需编译器、无需 SDK 头文件) @@ -139,9 +147,11 @@ cmake --build build --target example-dist ``` SessionExample.exe iotdb_session.dll +libssl-3-x64.dll +libcrypto-3-x64.dll ``` -(其他示例同理,可执行文件与 `iotdb_session.dll` 成对拷贝。) +(其他示例同理。SSL DLL 文件名与 SDK 中打包的 Tongsuo 主版本号一致。) **目标机器前置条件** @@ -160,7 +170,8 @@ iotdb_session.dll 若提示缺少 `VCRUNTIME140.dll`,请安装上述 VC++ 可再发行包。 -Thrift、Boost 已包含在 `iotdb_session.dll` 内,无需单独部署。 +Thrift、Boost 已包含在 `iotdb_session.dll` 内;SSL 由上述内置 Tongsuo 库提供, +无需单独部署系统 OpenSSL。 ### Linux @@ -169,9 +180,14 @@ Thrift、Boost 已包含在 `iotdb_session.dll` 内,无需单独部署。 ``` SessionExample libiotdb_session.so +libssl.so* +libcrypto.so* chmod +x SessionExample ``` +请一并拷贝 SDK `lib/` 目录中与 `libiotdb_session.so` 同目录的 `libssl` / +`libcrypto` 文件(Tongsuo,OpenSSL 兼容)。 + **目标机器前置条件** - 目标机的 **glibc 版本必须 ≥ 编译 SDK 时的 glibc 版本**(仅向后兼容: diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index 04f7fa1bd2db..fe92f791fb74 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -38,7 +38,7 @@ 3. Packages the produced install tree (maven-assembly-plugin) Everything else - thrift download, code generation, Boost/m4/flex/bison - bootstrap, OpenSSL discovery - lives in CMake modules under cmake/. + bootstrap, Tongsuo build - lives in CMake modules under cmake/. --> https://github.com/catchorg/Catch2/releases/download/v2.13.7/catch.hpp @@ -50,7 +50,8 @@ ${project.basedir}/third-party OFF ON - OFF + 6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242 + 8.4-stable ON @@ -113,7 +114,8 @@ - + + diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md index e321c6fe9847..0000e5559b51 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md @@ -31,9 +31,9 @@ the [`NOTICE`](NOTICE) file in this directory; non-Apache license texts are unde | Component | Version | How | License | | --- | --- | --- | --- | -| Apache Thrift | 0.23.0 | statically linked | Apache License 2.0 | +| Apache Thrift | 6dfb0b26 (post-0.23.0) | statically linked | Apache License 2.0 | | Boost | 1.60.0 on Linux/Windows, 1.84.0 on macOS by default | statically linked (header-only) | Boost Software License 1.0 | -| OpenSSL | 3.x: system OpenSSL 3.x when present, else 3.5.0 built from source (`WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 | +| Tongsuo | 8.4-stable (always built from source when `WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 | ## Build-time only (not redistributed) diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE index 4da431faa062..39bb234d1b44 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE @@ -19,10 +19,12 @@ This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ------------------------------------------------------------------------------ -OpenSSL (bundled shared libraries: libssl / libcrypto, present only when the +Tongsuo (bundled shared libraries: libssl / libcrypto, present only when the SDK is built with SSL support) -Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. +Copyright The Tongsuo Project Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (see the top-level LICENSE). +Tongsuo is an OpenSSL-compatible cryptographic library with additional Chinese +commercial cipher and TLCP protocol support. ------------------------------------------------------------------------------ Boost C++ Libraries (header-only; used at build time to compile Apache Thrift diff --git a/iotdb-client/client-cpp/third-party/README.md b/iotdb-client/client-cpp/third-party/README.md index 4cbdd1ed5692..1b7ebe266c9d 100644 --- a/iotdb-client/client-cpp/third-party/README.md +++ b/iotdb-client/client-cpp/third-party/README.md @@ -68,8 +68,8 @@ Alternatively copy files manually from the URLs listed in | Platform | Typical files | |------------|---------------| -| `linux/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (+ `openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is present) | -| `mac/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz` (Xcode CLT usually provides m4/flex/bison) | -| `windows/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison already on `PATH`) | +| `linux/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4-stable.tar.gz` (when `WITH_SSL=ON`, default) | +| `mac/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz` (Xcode CLT usually provides m4/flex/bison) | +| `windows/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison already on `PATH`) | Download URLs: see the *Offline build* table in [`README.md`](../README.md). From 1b2e20a856a082adbf4f047d8f8671ee29d8c00e Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 19:32:20 +0800 Subject: [PATCH 02/24] Add TLS/TLCP SSL support to the C++ client and fix Windows/Linux IT packaging. Expose PKCS12 trust/key store configuration across Session APIs, patch Thrift for custom SSL contexts, add SSL unit tests with fixtures, and copy bundled Tongsuo runtime libraries next to test/example binaries on Windows. --- iotdb-client/client-cpp/README.md | 80 +++ iotdb-client/client-cpp/README_zh.md | 58 ++ .../client-cpp/cmake/FetchThrift.cmake | 4 +- .../client-cpp/cmake/PatchThriftSsl.cmake | 80 +++ .../client-cpp/examples/CMakeLists.txt | 9 + iotdb-client/client-cpp/examples/README.md | 22 + iotdb-client/client-cpp/examples/README_zh.md | 20 + .../src/include/AbstractSessionBuilder.h | 6 + iotdb-client/client-cpp/src/include/Session.h | 3 + .../client-cpp/src/include/SessionBuilder.h | 25 + .../client-cpp/src/include/SessionC.h | 19 + .../client-cpp/src/include/SessionPool.h | 37 +- .../client-cpp/src/include/TableSession.h | 3 + .../src/include/TableSessionBuilder.h | 25 + .../client-cpp/src/rpc/NodesSupplier.cpp | 26 +- .../client-cpp/src/rpc/NodesSupplier.h | 15 +- .../client-cpp/src/rpc/RpcSslUtils.cpp | 678 ++++++++++++++++++ iotdb-client/client-cpp/src/rpc/RpcSslUtils.h | 68 ++ .../client-cpp/src/rpc/SessionConnection.cpp | 13 +- .../client-cpp/src/rpc/SessionConnection.h | 2 +- iotdb-client/client-cpp/src/rpc/SessionImpl.h | 4 +- .../client-cpp/src/rpc/ThriftConnection.cpp | 11 +- .../client-cpp/src/rpc/ThriftConnection.h | 6 +- .../client-cpp/src/session/Session.cpp | 19 +- .../client-cpp/src/session/SessionC.cpp | 195 ++++- .../client-cpp/src/session/SessionPool.cpp | 30 + .../client-cpp/src/session/TableSession.cpp | 5 + iotdb-client/client-cpp/test/CMakeLists.txt | 46 +- .../test/cpp/RpcSslMutualAuthTest.cpp | 193 +++++ .../client-cpp/test/cpp/RpcSslUtilsTest.cpp | 67 ++ .../client-cpp/test/cpp/SslTestFixtures.cpp | 644 +++++++++++++++++ .../client-cpp/test/cpp/SslTestFixtures.h | 79 ++ .../test/cpp/sessionCRelationalIT.cpp | 6 +- .../client-cpp/test/fixtures/.gitignore | 22 + .../client-cpp/test/fixtures/README.md | 23 + .../test/fixtures/generate_fixtures.cmd | 69 ++ .../client-cpp/test/fixtures/tlcp/ca.crt | 11 + .../test/fixtures/tlcp/client_enc.crt | 9 + .../test/fixtures/tlcp/client_enc.key | 8 + .../test/fixtures/tlcp/client_sign.crt | 9 + .../test/fixtures/tlcp/client_sign.key | 8 + .../test/fixtures/tlcp/server_enc.crt | 9 + .../test/fixtures/tlcp/server_enc.key | 8 + .../test/fixtures/tlcp/server_sign.crt | 9 + .../test/fixtures/tlcp/server_sign.key | 8 + .../test/fixtures/tlcp/tlcp-client-enc.p12 | Bin 0 -> 1029 bytes .../test/fixtures/tlcp/tlcp-client-sign.p12 | Bin 0 -> 1031 bytes .../test/fixtures/tlcp/tlcp-trust.p12 | Bin 0 -> 683 bytes .../client-cpp/test/fixtures/tls/ca.crt | 19 + .../client-cpp/test/fixtures/tls/client.crt | 17 + .../client-cpp/test/fixtures/tls/client.key | 28 + .../client-cpp/test/fixtures/tls/server.crt | 17 + .../client-cpp/test/fixtures/tls/server.key | 28 + .../test/fixtures/tls/tls-client.p12 | Bin 0 -> 2512 bytes .../test/fixtures/tls/tls-server.p12 | Bin 0 -> 2496 bytes .../test/fixtures/tls/tls-trust.p12 | Bin 0 -> 1083 bytes iotdb-client/client-cpp/test/main_rpc_ssl.cpp | 21 + .../client-cpp/test/tools/GenTlcpDualP12.cpp | 159 ++++ 58 files changed, 2912 insertions(+), 68 deletions(-) create mode 100644 iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake create mode 100644 iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp create mode 100644 iotdb-client/client-cpp/src/rpc/RpcSslUtils.h create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/SslTestFixtures.h create mode 100644 iotdb-client/client-cpp/test/fixtures/.gitignore create mode 100644 iotdb-client/client-cpp/test/fixtures/README.md create mode 100644 iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/client.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/client.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/server.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/server.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-client.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 create mode 100644 iotdb-client/client-cpp/test/main_rpc_ssl.cpp create mode 100644 iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index b969210dece8..96ca17a1ad3f 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -525,6 +525,86 @@ Host prerequisites when `WITH_SSL=ON`: - **Windows** – Perl (e.g. Strawberry Perl) and `nmake` from the Visual Studio Developer Command Prompt. +### Client SSL / TLCP configuration + +The C++ client mirrors the Java Session API. Use **PKCS12** (`.p12` / `.pfx`) +for `trustStore` and `keyStore`. JKS files must be converted to PKCS12 first +(the C++ client does not parse JKS). + +**TLS one-way (server authentication):** + +```cpp +#include "SessionBuilder.h" + +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLS mutual authentication:** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/keystore.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +**TLCP one-way (NTLS, GM/T):** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP mutual authentication** (dual SM2 certificates in PKCS12 `keyStore`): + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client-dual.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +The legacy `trustCertFilePath()` setter still works as an alias for a PEM CA +file when `trustStore` is not set. + +**C API** (configure before `ts_session_open` / `ts_table_session_open`): + +```c +CSession* session = ts_session_new("127.0.0.1", 6667, "root", "root"); +ts_session_set_use_ssl(session, true); +ts_session_set_ssl_protocol(session, "TLCP"); +ts_session_set_trust_store(session, "/path/to/ca.p12", "thrift"); +ts_session_set_key_store(session, "/path/to/client-dual.p12", "thrift"); +ts_session_open(session); +``` + ## Tests Maven binds `cmake-maven-plugin`'s `test` goal to the `integration-test` diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index 30aa89048bee..8635e4f56b11 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -248,6 +248,64 @@ SSL 默认开启(`WITH_SSL=ON`)。配置阶段**始终从源码构建** (OpenSSL 兼容 API,Apache-2.0,支持国密/TLCP),并把 `libssl`/`libcrypto` 动态库复制到产物 `lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。 + +### 客户端 SSL / TLCP 配置 + +C++ 客户端 API 与 Java Session 对齐。`trustStore` 与 `keyStore` 请使用 +**PKCS12**(`.p12` / `.pfx`)。JKS 需先转换为 PKCS12(C++ 端不解析 JKS)。 + +**TLS 单向认证:** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP 单向认证(国密 NTLS):** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP 双向认证**(PKCS12 `keyStore` 内含 SM2 签名/加密双证书): + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client-dual.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +旧版 `trustCertFilePath()` 在未设置 `trustStore` 时仍可作为 PEM CA 路径使用。 + +**C API**(在 `ts_session_open` / `ts_table_session_open` 之前配置): + +```c +ts_session_set_use_ssl(session, true); +ts_session_set_ssl_protocol(session, "TLCP"); +ts_session_set_trust_store(session, "/path/to/ca.p12", "thrift"); +ts_session_set_key_store(session, "/path/to/client-dual.p12", "thrift"); +``` + Debug 构建请在配置阶段传入 `-DCMAKE_BUILD_TYPE=Debug`。Windows 使用 Visual Studio 生成器时也需要传入该选项,以便内置 Thrift 静态库使用 Debug MSVC 运行时; 随后用 `cmake --build build --config Debug --target install` 构建安装。 diff --git a/iotdb-client/client-cpp/cmake/FetchThrift.cmake b/iotdb-client/client-cpp/cmake/FetchThrift.cmake index cc611b340134..7ac7dd6838bf 100644 --- a/iotdb-client/client-cpp/cmake/FetchThrift.cmake +++ b/iotdb-client/client-cpp/cmake/FetchThrift.cmake @@ -111,6 +111,8 @@ if(NOT EXISTS "${_thrift_src}/CMakeLists.txt") "extracting ${_thrift_tarball}.") endif() +include("${CMAKE_CURRENT_LIST_DIR}/PatchThriftSsl.cmake") + # --------------------------------------------------------------------------- # ExternalProject_Add: build thrift at *configure* time so the produced # binary / library can immediately drive code generation and linking. @@ -185,7 +187,7 @@ if(WITH_SSL) else() set(_thrift_ssl_stamp "-nossl") endif() -set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_GIT_COMMIT}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}") +set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_GIT_COMMIT}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}-sslctx") if(NOT EXISTS "${_thrift_stamp}") file(MAKE_DIRECTORY "${_thrift_build}") message(STATUS "[Thrift] configuring ${_thrift_dirname}") diff --git a/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake b/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake new file mode 100644 index 000000000000..ff5394c1b30e --- /dev/null +++ b/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake @@ -0,0 +1,80 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# ============================================================================= +# PatchThriftSsl.cmake +# +# Extends the vendored Apache Thrift C++ SSL transport with SSLContextFactory and +# SSLContext(SSL_CTX*) so IoTDB can inject custom OpenSSL / NTLS contexts. +# ============================================================================= + +if(NOT WITH_SSL) + return() +endif() + +set(_thrift_ssl_header "${_thrift_src}/lib/cpp/src/thrift/transport/TSSLSocket.h") +set(_thrift_ssl_cpp "${_thrift_src}/lib/cpp/src/thrift/transport/TSSLSocket.cpp") +set(_thrift_ssl_patch_marker "${_thrift_root}/.patched-ssl-context-${THRIFT_GIT_COMMIT}") + +if(EXISTS "${_thrift_ssl_patch_marker}") + return() +endif() + +if(NOT EXISTS "${_thrift_ssl_header}") + message(FATAL_ERROR "[Thrift] cannot patch missing ${_thrift_ssl_header}") +endif() + +file(READ "${_thrift_ssl_header}" _thrift_ssl_header_content) +if(NOT _thrift_ssl_header_content MATCHES "SSLContextFactory") + if(NOT _thrift_ssl_header_content MATCHES "#include ") + string(REPLACE + "#include " + "#include \n#include " + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + endif() + string(REPLACE + "class SSLContext;" + "class SSLContext;\ntypedef std::function()> SSLContextFactory;" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + string(REPLACE + " TSSLSocketFactory(SSLProtocol protocol = SSLTLS);" + " TSSLSocketFactory(SSLProtocol protocol = SSLTLS);\n /**\n * Constructor\n *\n * @param contextFactory Function invoked during construction to return a custom OpenSSL context.\n */\n TSSLSocketFactory(const SSLContextFactory& contextFactory);" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + string(REPLACE + " SSLContext(const SSLProtocol& protocol = SSLTLS);" + " SSLContext(const SSLProtocol& protocol = SSLTLS);\n /**\n * Wrap an existing OpenSSL SSL_CTX.\n *\n * Takes ownership of @a ctx; the caller must not call SSL_CTX_free on it.\n */\n explicit SSLContext(SSL_CTX* ctx);" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + file(WRITE "${_thrift_ssl_header}" "${_thrift_ssl_header_content}") +endif() + +if(EXISTS "${_thrift_ssl_cpp}") + file(READ "${_thrift_ssl_cpp}" _thrift_ssl_cpp_content) + if(NOT _thrift_ssl_cpp_content MATCHES "SSLContext::SSLContext\\(SSL_CTX\\* ctx\\)") + string(REPLACE + "SSLContext::~SSLContext() {" + "SSLContext::SSLContext(SSL_CTX* ctx) : ctx_(ctx) {\n if (ctx_ == nullptr) {\n string errors;\n buildErrors(errors);\n throw TSSLException(\"SSL_CTX_new: null context\");\n }\n}\n\nSSLContext::~SSLContext() {" + _thrift_ssl_cpp_content "${_thrift_ssl_cpp_content}") + string(REPLACE + "TSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) {" + "TSSLSocketFactory::TSSLSocketFactory(const SSLContextFactory& contextFactory) : server_(false) {\n Guard guard(mutex_);\n if (count_ == 0) {\n if (!manualOpenSSLInitialization_) {\n didWeInitializeOpenSSL_ = true;\n initializeOpenSSL();\n }\n randomize();\n }\n count_++;\n ctx_ = contextFactory();\n}\n\nTSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) {" + _thrift_ssl_cpp_content "${_thrift_ssl_cpp_content}") + file(WRITE "${_thrift_ssl_cpp}" "${_thrift_ssl_cpp_content}") + endif() +endif() + +file(TOUCH "${_thrift_ssl_patch_marker}") +message(STATUS "[Thrift] applied SSLContextFactory patch to ${_thrift_ssl_header}") diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index ed80e15b8c75..7fa5bae86661 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -180,6 +180,15 @@ foreach(_t IN LISTS _example_targets) COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMENT "Copy IoTDB runtime library next to ${_t}") + if(WIN32 AND WITH_SSL) + _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls) + foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_dll}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endforeach() + endif() elseif(EXISTS "${_iotdb_runtime}") add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/iotdb-client/client-cpp/examples/README.md b/iotdb-client/client-cpp/examples/README.md index d86f6b6202b5..945686d4e2a0 100644 --- a/iotdb-client/client-cpp/examples/README.md +++ b/iotdb-client/client-cpp/examples/README.md @@ -248,6 +248,28 @@ version should be **≥ the deployment target used to build the SDK**. Check wit otool -L SessionExample ``` +## SSL / TLCP examples + +When connecting to an SSL-enabled IoTDB DataNode, configure the session builder +before `build()`: + +```cpp +SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") // or "TLCP" for NTLS / GM/T + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client.p12") // optional, mutual auth + ->keyStorePwd("thrift") + ->build(); +``` + +Use PKCS12 stores (convert JKS with `keytool -importkeystore`). See the main +[README.md](../README.md#client-ssl--tlcp-configuration) for TLS and TLCP +details. + ## Development notes - **Windows**: Application and SDK both use **`/MD`** (dynamic CRT). This diff --git a/iotdb-client/client-cpp/examples/README_zh.md b/iotdb-client/client-cpp/examples/README_zh.md index b70fa7107c41..233f575943d5 100644 --- a/iotdb-client/client-cpp/examples/README_zh.md +++ b/iotdb-client/client-cpp/examples/README_zh.md @@ -242,6 +242,26 @@ export LD_LIBRARY_PATH=. otool -L SessionExample ``` +## SSL / TLCP 示例 + +连接已启用 SSL 的 DataNode 时,在 `build()` 前配置: + +```cpp +SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") // 国密请使用 "TLCP" + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client.p12") // 可选,双向认证 + ->keyStorePwd("thrift") + ->build(); +``` + +请使用 PKCS12 证书库(JKS 可用 `keytool -importkeystore` 转换)。详见 +[README.md](../README.md#client-ssl--tlcp-configuration)。 + ## 开发说明 - **Windows**:应用与 SDK 均使用 **`/MD`**,与 Visual Studio 默认工程一致; diff --git a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h index 3735dfa227d0..6217a2e73761 100644 --- a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h @@ -55,7 +55,13 @@ class AbstractSessionBuilder { bool enableRPCCompression = DEFAULT_ENABLE_RPC_COMPRESSION; std::vector nodeUrls; bool useSSL = false; + /** @deprecated Use trustStore() instead. Legacy PEM trust certificate path. */ std::string trustCertFilePath; + std::string sslProtocol = "TLS"; + std::string trustStore; + std::string trustStorePwd; + std::string keyStore; + std::string keyStorePwd; }; #endif // IOTDB_ABSTRACTSESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/Session.h b/iotdb-client/client-cpp/src/include/Session.h index a0910584e577..963725589a5d 100644 --- a/iotdb-client/client-cpp/src/include/Session.h +++ b/iotdb-client/client-cpp/src/include/Session.h @@ -19,6 +19,8 @@ #ifndef IOTDB_SESSION_H #define IOTDB_SESSION_H +struct SslConfig; + #include #include #include @@ -600,6 +602,7 @@ class Session { void setSqlDialect(const std::string& dialect); void setDatabase(const std::string& database); + void setSslConfig(const SslConfig& sslConfig); std::string getDatabase(); void changeDatabase(const std::string& database); diff --git a/iotdb-client/client-cpp/src/include/SessionBuilder.h b/iotdb-client/client-cpp/src/include/SessionBuilder.h index 14342697eb5d..5d3eabd7434e 100644 --- a/iotdb-client/client-cpp/src/include/SessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/SessionBuilder.h @@ -44,6 +44,31 @@ class SessionBuilder : public AbstractSessionBuilder { return this; } + SessionBuilder* sslProtocol(const std::string& sslProtocol) { + AbstractSessionBuilder::sslProtocol = sslProtocol; + return this; + } + + SessionBuilder* trustStore(const std::string& trustStore) { + AbstractSessionBuilder::trustStore = trustStore; + return this; + } + + SessionBuilder* trustStorePwd(const std::string& trustStorePwd) { + AbstractSessionBuilder::trustStorePwd = trustStorePwd; + return this; + } + + SessionBuilder* keyStore(const std::string& keyStore) { + AbstractSessionBuilder::keyStore = keyStore; + return this; + } + + SessionBuilder* keyStorePwd(const std::string& keyStorePwd) { + AbstractSessionBuilder::keyStorePwd = keyStorePwd; + return this; + } + SessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/include/SessionC.h b/iotdb-client/client-cpp/src/include/SessionC.h index fdce5801a9de..32dce6e626bd 100644 --- a/iotdb-client/client-cpp/src/include/SessionC.h +++ b/iotdb-client/client-cpp/src/include/SessionC.h @@ -131,6 +131,15 @@ TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompr TsStatus ts_session_close(CSession* session); +TsStatus ts_session_set_use_ssl(CSession* session, bool useSsl); +TsStatus ts_session_set_ssl_protocol(CSession* session, const char* sslProtocol); +TsStatus ts_session_set_trust_store(CSession* session, const char* trustStore, + const char* trustStorePwd); +TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, + const char* keyStorePwd); +/** @deprecated Use ts_session_set_trust_store() instead. */ +TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath); + /* ============================================================ * Session Lifecycle — Table Model * ============================================================ */ @@ -148,6 +157,16 @@ TsStatus ts_table_session_open(CTableSession* session); TsStatus ts_table_session_close(CTableSession* session); +TsStatus ts_table_session_set_use_ssl(CTableSession* session, bool useSsl); +TsStatus ts_table_session_set_ssl_protocol(CTableSession* session, const char* sslProtocol); +TsStatus ts_table_session_set_trust_store(CTableSession* session, const char* trustStore, + const char* trustStorePwd); +TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyStore, + const char* keyStorePwd); +/** @deprecated Use ts_table_session_set_trust_store() instead. */ +TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, + const char* trustCertFilePath); + /* ============================================================ * Timezone * ============================================================ */ diff --git a/iotdb-client/client-cpp/src/include/SessionPool.h b/iotdb-client/client-cpp/src/include/SessionPool.h index 4483dab0c514..c71580262446 100644 --- a/iotdb-client/client-cpp/src/include/SessionPool.h +++ b/iotdb-client/client-cpp/src/include/SessionPool.h @@ -188,6 +188,11 @@ class SessionPool { SessionPool& setWaitToGetSessionTimeoutMs(int64_t timeoutMs); SessionPool& setUseSSL(bool useSSL); SessionPool& setTrustCertFilePath(std::string path); + SessionPool& setSslProtocol(std::string sslProtocol); + SessionPool& setTrustStore(std::string trustStore); + SessionPool& setTrustStorePwd(std::string trustStorePwd); + SessionPool& setKeyStore(std::string keyStore); + SessionPool& setKeyStorePwd(std::string keyStorePwd); // Borrow a Session. Blocks until one is free or a new one can be created, // up to timeoutMs (<= 0 means use the pool default). Throws IoTDBException on @@ -249,6 +254,11 @@ class SessionPool { int connectTimeoutMs_ = AbstractSessionBuilder::DEFAULT_CONNECT_TIMEOUT_MS; bool useSSL_ = false; std::string trustCertFilePath_; + std::string sslProtocol_ = "TLS"; + std::string trustStore_; + std::string trustStorePwd_; + std::string keyStore_; + std::string keyStorePwd_; // pool sizing / waiting policy size_t maxSize_; @@ -339,6 +349,26 @@ class SessionPoolBuilder : public AbstractSessionBuilder { AbstractSessionBuilder::trustCertFilePath = v; return this; } + SessionPoolBuilder* sslProtocol(const std::string& v) { + AbstractSessionBuilder::sslProtocol = v; + return this; + } + SessionPoolBuilder* trustStore(const std::string& v) { + AbstractSessionBuilder::trustStore = v; + return this; + } + SessionPoolBuilder* trustStorePwd(const std::string& v) { + AbstractSessionBuilder::trustStorePwd = v; + return this; + } + SessionPoolBuilder* keyStore(const std::string& v) { + AbstractSessionBuilder::keyStore = v; + return this; + } + SessionPoolBuilder* keyStorePwd(const std::string& v) { + AbstractSessionBuilder::keyStorePwd = v; + return this; + } SessionPoolBuilder* maxSize(size_t v) { maxSize_ = v; return this; @@ -380,7 +410,12 @@ class SessionPoolBuilder : public AbstractSessionBuilder { .setConnectTimeoutMs(AbstractSessionBuilder::connectTimeoutMs) .setWaitToGetSessionTimeoutMs(waitTimeoutMs_) .setUseSSL(AbstractSessionBuilder::useSSL) - .setTrustCertFilePath(AbstractSessionBuilder::trustCertFilePath); + .setTrustCertFilePath(AbstractSessionBuilder::trustCertFilePath) + .setSslProtocol(AbstractSessionBuilder::sslProtocol) + .setTrustStore(AbstractSessionBuilder::trustStore) + .setTrustStorePwd(AbstractSessionBuilder::trustStorePwd) + .setKeyStore(AbstractSessionBuilder::keyStore) + .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd); return pool; } diff --git a/iotdb-client/client-cpp/src/include/TableSession.h b/iotdb-client/client-cpp/src/include/TableSession.h index d1eecfeeabae..a57288339b6f 100644 --- a/iotdb-client/client-cpp/src/include/TableSession.h +++ b/iotdb-client/client-cpp/src/include/TableSession.h @@ -24,6 +24,8 @@ #include "Session.h" +struct SslConfig; + class TableSession { private: std::shared_ptr session_; @@ -41,6 +43,7 @@ class TableSession { unique_ptr executeQueryStatement(const std::string& sql, int64_t timeoutInMs); void open(bool enableRPCCompression = false); void close(); + void setSslConfig(const SslConfig& sslConfig); }; #endif // IOTDB_TABLESESSION_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h index 3c9739ecc8ed..0642acf0759b 100644 --- a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h @@ -55,6 +55,31 @@ class TableSessionBuilder : public AbstractSessionBuilder { return this; } + TableSessionBuilder* sslProtocol(const std::string& sslProtocol) { + AbstractSessionBuilder::sslProtocol = sslProtocol; + return this; + } + + TableSessionBuilder* trustStore(const std::string& trustStore) { + AbstractSessionBuilder::trustStore = trustStore; + return this; + } + + TableSessionBuilder* trustStorePwd(const std::string& trustStorePwd) { + AbstractSessionBuilder::trustStorePwd = trustStorePwd; + return this; + } + + TableSessionBuilder* keyStore(const std::string& keyStore) { + AbstractSessionBuilder::keyStore = keyStore; + return this; + } + + TableSessionBuilder* keyStorePwd(const std::string& keyStorePwd) { + AbstractSessionBuilder::keyStorePwd = keyStorePwd; + return this; + } + TableSessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp index 604099a82d1b..476e47a84433 100644 --- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp +++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp @@ -17,6 +17,7 @@ * under the License. */ #include "NodesSupplier.h" +#include "RpcSslUtils.h" #include "Session.h" #include "SessionDataSet.h" #include @@ -68,31 +69,31 @@ StaticNodesSupplier::~StaticNodesSupplier() = default; std::shared_ptr NodesSupplier::create( const std::vector& endpoints, const std::string& userName, - const std::string& password, bool useSSL, const std::string& trustCertFilePath, - const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, - int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, - std::chrono::milliseconds refreshInterval, NodeSelectionPolicy policy) { + const std::string& password, const SslConfig& sslConfig, const std::string& zoneId, + int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, + bool enableRPCCompression, const std::string& version, std::chrono::milliseconds refreshInterval, + NodeSelectionPolicy policy) { if (endpoints.empty()) { return nullptr; } auto supplier = std::make_shared( - userName, password, useSSL, trustCertFilePath, zoneId, thriftDefaultBufferSize, - thriftMaxFrameSize, connectionTimeoutInMs, enableRPCCompression, version, endpoints, policy); + userName, password, sslConfig, zoneId, thriftDefaultBufferSize, thriftMaxFrameSize, + connectionTimeoutInMs, enableRPCCompression, version, endpoints, policy); supplier->startBackgroundRefresh(refreshInterval); return supplier; } -NodesSupplier::NodesSupplier(const std::string& userName, const std::string& password, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, +NodesSupplier::NodesSupplier(const std::string& userName, const std::string& password, + const SslConfig& sslConfig, const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, const std::vector& endpoints, NodeSelectionPolicy policy) : userName_(userName), password_(password), zoneId_(zoneId), thriftDefaultBufferSize_(thriftDefaultBufferSize), thriftMaxFrameSize_(thriftMaxFrameSize), - connectionTimeoutInMs_(connectionTimeoutInMs), useSSL_(useSSL), - trustCertFilePath_(trustCertFilePath), enableRPCCompression_(enableRPCCompression), - version_(version), endpoints_(endpoints), selectionPolicy_(policy) { + connectionTimeoutInMs_(connectionTimeoutInMs), sslConfig_(sslConfig), + enableRPCCompression_(enableRPCCompression), version_(version), endpoints_(endpoints), + selectionPolicy_(policy) { deduplicateEndpoints(); } @@ -155,8 +156,7 @@ std::vector NodesSupplier::fetchLatestEndpoints() { try { if (client_ == nullptr) { client_ = std::make_shared(endpoint); - client_->init(userName_, password_, enableRPCCompression_, useSSL_, trustCertFilePath_, - zoneId_, version_); + client_->init(userName_, password_, enableRPCCompression_, sslConfig_, zoneId_, version_); } auto sessionDataSet = client_->executeQueryStatement(SHOW_AVAILABLE_URLS_COMMAND); diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h index c067bbb6d722..b667c95b175d 100644 --- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h +++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h @@ -30,6 +30,7 @@ #include #include "ThriftConnection.h" +#include "RpcSslUtils.h" class TEndPoint; @@ -78,8 +79,8 @@ class NodesSupplier : public INodesSupplier { static std::shared_ptr create(const std::vector& endpoints, const std::string& userName, - const std::string& password, bool useSSL = false, - const std::string& trustCertFilePath = "", const std::string& zoneId = "", + const std::string& password, const SslConfig& sslConfig, + const std::string& zoneId = "", int32_t thriftDefaultBufferSize = ThriftConnection::THRIFT_DEFAULT_BUFFER_SIZE, int32_t thriftMaxFrameSize = ThriftConnection::THRIFT_MAX_FRAME_SIZE, int32_t connectionTimeoutInMs = ThriftConnection::CONNECTION_TIMEOUT_IN_MS, @@ -87,10 +88,9 @@ class NodesSupplier : public INodesSupplier { std::chrono::milliseconds refreshInterval = std::chrono::milliseconds(TIMEOUT_IN_MS), NodeSelectionPolicy policy = RoundRobinPolicy::select); - NodesSupplier(const std::string& userName, const std::string& password, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, - int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, - int32_t connectionTimeoutInMs, bool enableRPCCompression, + NodesSupplier(const std::string& userName, const std::string& password, const SslConfig& sslConfig, + const std::string& zoneId, int32_t thriftDefaultBufferSize, + int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, const std::vector& endpoints, NodeSelectionPolicy policy); @@ -106,8 +106,7 @@ class NodesSupplier : public INodesSupplier { int32_t thriftDefaultBufferSize_; int32_t thriftMaxFrameSize_; int32_t connectionTimeoutInMs_; - bool useSSL_; - std::string trustCertFilePath_; + SslConfig sslConfig_; bool enableRPCCompression_; std::string version_; std::string zoneId_; diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp new file mode 100644 index 000000000000..3410cabc2c24 --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -0,0 +1,678 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#if WITH_SSL +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "RpcSslUtils.h" + +#include "Common.h" + +#include +#include +#include +#include +#include + +namespace { + +std::string gDefaultProtocol = RpcSslUtils::DEFAULT_PROTOCOL; + +std::string trimToEmpty(const std::string& value) { + const auto start = value.find_first_not_of(" \t\r\n"); + if (start == std::string::npos) { + return ""; + } + const auto end = value.find_last_not_of(" \t\r\n"); + return value.substr(start, end - start + 1); +} + +bool hasText(const std::string& value) { + return !trimToEmpty(value).empty(); +} + +std::string toUpper(const std::string& value) { + std::string out = value; + std::transform(out.begin(), out.end(), out.begin(), + [](unsigned char c) { return static_cast(std::toupper(c)); }); + return out; +} + +bool endsWithIgnoreCase(const std::string& value, const std::string& suffix) { + if (value.size() < suffix.size()) { + return false; + } + const std::string tail = value.substr(value.size() - suffix.size()); + return toUpper(tail) == toUpper(suffix); +} + +bool isPkcs12Path(const std::string& path) { + return endsWithIgnoreCase(path, ".p12") || endsWithIgnoreCase(path, ".pfx"); +} + +#if WITH_SSL + +std::string collectOpenSslErrors() { + std::string errors; + unsigned long errCode = 0; + while ((errCode = ERR_get_error()) != 0) { + char buf[256]; + ERR_error_string_n(errCode, buf, sizeof(buf)); + if (!errors.empty()) { + errors.append("; "); + } + errors.append(buf); + } + return errors.empty() ? "unknown OpenSSL error" : errors; +} + +void throwSslError(const std::string& message) { + throw IoTDBException(message + ": " + collectOpenSslErrors()); +} + +void ensureFileReadable(const std::string& path, const std::string& label) { + if (!hasText(path)) { + throw IoTDBException(label + " path is empty"); + } + std::ifstream in(path.c_str(), std::ios::binary); + if (!in.good()) { + throw IoTDBException(label + " file not found: " + path); + } +} + +PKCS12* loadPkcs12(const std::string& path, const std::string& password) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + throwSslError("Failed to open PKCS12 file " + path); + } + PKCS12* p12 = d2i_PKCS12_bio(bio, nullptr); + BIO_free(bio); + if (p12 == nullptr) { + throwSslError("Failed to parse PKCS12 file " + path); + } + (void)password; + return p12; +} + +std::string getBagFriendlyName(PKCS12_SAFEBAG* bag) { + char* name = PKCS12_get_friendlyname(bag); + if (name == nullptr) { + return ""; + } + std::string friendlyName(name); + OPENSSL_free(name); + return friendlyName; +} + +void forEachPkcs12Bag(PKCS12* p12, const std::string& password, + const std::function& visitor) { + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(p12); + if (safes == nullptr) { + return; + } + for (int i = 0; i < sk_PKCS7_num(safes); ++i) { + PKCS7* p7 = sk_PKCS7_value(safes, i); + STACK_OF(PKCS12_SAFEBAG)* bags = nullptr; + if (PKCS7_type_is_data(p7)) { + bags = PKCS12_unpack_p7data(p7); + } else if (PKCS7_type_is_encrypted(p7)) { + bags = PKCS12_unpack_p7encdata(p7, password.c_str(), static_cast(password.size())); + } + if (bags == nullptr) { + continue; + } + for (int j = 0; j < sk_PKCS12_SAFEBAG_num(bags); ++j) { + visitor(sk_PKCS12_SAFEBAG_value(bags, j)); + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + sk_PKCS7_pop_free(safes, PKCS7_free); +} + +EVP_PKEY* extractBagPrivateKey(PKCS12_SAFEBAG* bag, const std::string& password) { + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + const PKCS8_PRIV_KEY_INFO* p8const = nullptr; + PKCS8_PRIV_KEY_INFO* p8owned = nullptr; + if (bagType == NID_pkcs8ShroudedKeyBag) { + p8owned = PKCS12_decrypt_skey(bag, password.c_str(), static_cast(password.size())); + p8const = p8owned; + } else if (bagType == NID_keyBag) { + p8const = PKCS12_SAFEBAG_get0_p8inf(bag); + } + if (p8const == nullptr) { + return nullptr; + } + EVP_PKEY* key = EVP_PKCS82PKEY(p8const); + if (p8owned != nullptr) { + PKCS8_PRIV_KEY_INFO_free(p8owned); + } + return key; +} + +bool friendlyNameContains(const std::string& friendlyName, const std::string& keyword) { + const std::string upperName = toUpper(friendlyName); + const std::string upperKeyword = toUpper(keyword); + return upperName.find(upperKeyword) != std::string::npos; +} + +void validateCertificate(X509* cert) { + if (cert == nullptr) { + return; + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (X509_cmp_current_time(X509_get0_notBefore(cert)) > 0 || + X509_cmp_current_time(X509_get0_notAfter(cert)) < 0) { + throw IoTDBException("Certificate is not currently valid"); + } +#else + if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0 || + X509_cmp_current_time(X509_get_notAfter(cert)) < 0) { + throw IoTDBException("Certificate is not currently valid"); + } +#endif +} + +void addCertToStore(X509_STORE* store, X509* cert) { + if (store == nullptr || cert == nullptr) { + return; + } + if (X509_STORE_add_cert(store, cert) != 1) { + const unsigned long errCode = ERR_peek_last_error(); + if (ERR_GET_LIB(errCode) != ERR_LIB_X509 || ERR_GET_REASON(errCode) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + throwSslError("Failed to add certificate to trust store"); + } + } +} + +void loadTrustFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + EVP_PKEY* pkey = nullptr; + X509* cert = nullptr; + STACK_OF(X509)* ca = nullptr; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { + PKCS12_free(p12); + throwSslError("Failed to parse PKCS12 trust store " + path); + } + + X509_STORE* store = SSL_CTX_get_cert_store(ctx); + if (cert != nullptr) { + validateCertificate(cert); + addCertToStore(store, cert); + X509_free(cert); + } + if (ca != nullptr) { + for (int i = 0; i < sk_X509_num(ca); ++i) { + X509* caCert = sk_X509_value(ca, i); + validateCertificate(caCert); + addCertToStore(store, caCert); + } + sk_X509_pop_free(ca, X509_free); + } + if (pkey != nullptr) { + EVP_PKEY_free(pkey); + } + + STACK_OF(PKCS12_SAFEBAG)* unusedBags = nullptr; + (void)unusedBags; + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { + X509* bagCert = PKCS12_certbag2x509(bag); + if (bagCert != nullptr) { + validateCertificate(bagCert); + addCertToStore(store, bagCert); + X509_free(bagCert); + } + } + }); + + PKCS12_free(p12); +} + +void loadTrustFromPem(SSL_CTX* ctx, const std::string& path) { + if (SSL_CTX_load_verify_locations(ctx, path.c_str(), nullptr) != 1) { + throwSslError("Failed to load PEM trust store " + path); + } +} + +void loadTrustStore(SSL_CTX* ctx, const std::string& path, const std::string& password) { + ensureFileReadable(path, "Trust store"); + if (isPkcs12Path(path)) { + loadTrustFromPkcs12(ctx, path, password); + } else { + loadTrustFromPem(ctx, path); + } +} + +void loadTlsIdentityFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + EVP_PKEY* pkey = nullptr; + X509* cert = nullptr; + STACK_OF(X509)* ca = nullptr; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { + PKCS12_free(p12); + throwSslError("Failed to parse PKCS12 key store " + path); + } + if (SSL_CTX_use_certificate(ctx, cert) != 1) { + throwSslError("Failed to load client certificate from " + path); + } + if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { + throwSslError("Failed to load client private key from " + path); + } + if (SSL_CTX_check_private_key(ctx) != 1) { + throwSslError("Client certificate and private key do not match in " + path); + } + if (ca != nullptr) { + sk_X509_pop_free(ca, X509_free); + } + if (cert != nullptr) { + X509_free(cert); + } + if (pkey != nullptr) { + EVP_PKEY_free(pkey); + } + PKCS12_free(p12); +} + +void loadTlsIdentityFromPem(SSL_CTX* ctx, const std::string& path) { + if (SSL_CTX_use_certificate_file(ctx, path.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client certificate from " + path); + } + if (SSL_CTX_use_PrivateKey_file(ctx, path.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client private key from " + path); + } + if (SSL_CTX_check_private_key(ctx) != 1) { + throwSslError("Client certificate and private key do not match in " + path); + } +} + +void loadTlsKeyStore(SSL_CTX* ctx, const std::string& path, const std::string& password) { + ensureFileReadable(path, "Key store"); + if (isPkcs12Path(path)) { + loadTlsIdentityFromPkcs12(ctx, path, password); + } else { + loadTlsIdentityFromPem(ctx, path); + } +} + +struct TlcpIdentity { + X509* signCert = nullptr; + EVP_PKEY* signKey = nullptr; + X509* encCert = nullptr; + EVP_PKEY* encKey = nullptr; +}; + +void freeTlcpIdentity(TlcpIdentity& identity) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + identity.signCert = nullptr; + } + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + identity.signKey = nullptr; + } + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + identity.encCert = nullptr; + } + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + identity.encKey = nullptr; + } +} + +void assignTlcpMaterial(TlcpIdentity& identity, const std::string& friendlyName, X509* cert, + EVP_PKEY* key) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + } + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + } + identity.encCert = cert; + identity.encKey = key; + return; + } + if (friendlyNameContains(friendlyName, "sign") || identity.signCert == nullptr) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + } + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + } + identity.signCert = cert; + identity.signKey = key; + return; + } + if (identity.encCert == nullptr) { + identity.encCert = cert; + identity.encKey = key; + return; + } + X509_free(cert); + EVP_PKEY_free(key); +} + +void loadTlcpKeyStoreFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + TlcpIdentity identity; + + EVP_PKEY* parsedKey = nullptr; + X509* parsedCert = nullptr; + STACK_OF(X509)* ca = nullptr; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsedKey, &parsedCert, + &ca) == 1) { + assignTlcpMaterial(identity, "sign", parsedCert, parsedKey); + parsedCert = nullptr; + parsedKey = nullptr; + } + if (ca != nullptr) { + sk_X509_pop_free(ca, X509_free); + } + + STACK_OF(PKCS12_SAFEBAG)* unusedBags = nullptr; + (void)unusedBags; + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + const std::string friendlyName = getBagFriendlyName(bag); + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + if (bagType == NID_certBag) { + X509* cert = PKCS12_certbag2x509(bag); + if (cert != nullptr) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + } + identity.encCert = cert; + } else if (friendlyNameContains(friendlyName, "sign") || identity.signCert == nullptr) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + } + identity.signCert = cert; + } else if (identity.encCert == nullptr) { + identity.encCert = cert; + } else { + X509_free(cert); + } + } + } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { + EVP_PKEY* key = extractBagPrivateKey(bag, password); + if (key != nullptr) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + } + identity.encKey = key; + } else if (friendlyNameContains(friendlyName, "sign") || identity.signKey == nullptr) { + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + } + identity.signKey = key; + } else if (identity.encKey == nullptr) { + identity.encKey = key; + } else { + EVP_PKEY_free(key); + } + } + } + }); + PKCS12_free(p12); + + if (identity.signCert == nullptr || identity.signKey == nullptr) { + freeTlcpIdentity(identity); + throw IoTDBException("TLCP PKCS12 key store must contain a signing certificate and key: " + path); + } + + if (SSL_CTX_use_sign_certificate(ctx, identity.signCert) != 1 || + SSL_CTX_use_sign_PrivateKey(ctx, identity.signKey) != 1) { + freeTlcpIdentity(identity); + throwSslError("Failed to load TLCP signing credentials from " + path); + } + + if (identity.encCert != nullptr && identity.encKey != nullptr) { + if (SSL_CTX_use_enc_certificate(ctx, identity.encCert) != 1 || + SSL_CTX_use_enc_PrivateKey(ctx, identity.encKey) != 1) { + freeTlcpIdentity(identity); + throwSslError("Failed to load TLCP encryption credentials from " + path); + } + } + + freeTlcpIdentity(identity); +} + +void applyTlsProtocolVersion(SSL_CTX* ctx, const std::string& protocol) { + const std::string resolved = RpcSslUtils::normalizeProtocol(protocol); + const std::string upper = toUpper(resolved); + if (upper == "TLSV1.2") { + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); + return; + } + if (upper == "TLSV1.3") { + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + return; + } + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); +} + +SSL_CTX* createTlsClientContext(const SslConfig& config) { + const std::string protocol = RpcSslUtils::resolveProtocol(config.sslProtocol); + SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); + if (ctx == nullptr) { + throwSslError("Failed to create TLS client context"); + } + applyTlsProtocolVersion(ctx, protocol); + + const std::string trustStore = config.effectiveTrustStore(); + if (hasText(trustStore)) { + loadTrustStore(ctx, trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + if (hasText(config.keyStore)) { + loadTlsKeyStore(ctx, config.keyStore, config.keyStorePwd); + } + return ctx; +} + +SSL_CTX* createTlcpClientContext(const SslConfig& config) { + SSL_CTX* ctx = SSL_CTX_new(NTLS_client_method()); + if (ctx == nullptr) { + throwSslError("Failed to create TLCP client context"); + } + SSL_CTX_enable_ntls(ctx); + if (SSL_CTX_set_cipher_list(ctx, RpcSslUtils::DEFAULT_TLCP_CIPHER) != 1) { + SSL_CTX_free(ctx); + throwSslError("Failed to set TLCP cipher suite"); + } + + const std::string trustStore = config.effectiveTrustStore(); + if (hasText(trustStore)) { + loadTrustStore(ctx, trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + if (hasText(config.keyStore)) { + loadTlcpKeyStoreFromPkcs12(ctx, config.keyStore, config.keyStorePwd); + } + return ctx; +} + +void validatePkcs12Store(const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + EVP_PKEY* pkey = nullptr; + X509* cert = nullptr; + STACK_OF(X509)* ca = nullptr; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { + PKCS12_free(p12); + throw IoTDBException("Failed to parse PKCS12 store: " + path); + } + if (cert != nullptr) { + validateCertificate(cert); + X509_free(cert); + } + if (ca != nullptr) { + for (int i = 0; i < sk_X509_num(ca); ++i) { + validateCertificate(sk_X509_value(ca, i)); + } + sk_X509_pop_free(ca, X509_free); + } + if (pkey != nullptr) { + EVP_PKEY_free(pkey); + } + + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { + X509* bagCert = PKCS12_certbag2x509(bag); + if (bagCert != nullptr) { + validateCertificate(bagCert); + X509_free(bagCert); + } + } + }); + PKCS12_free(p12); +} + +void validatePemStore(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + throw IoTDBException("Store file not found: " + path); + } + bool foundCert = false; + while (true) { + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (cert == nullptr) { + break; + } + validateCertificate(cert); + X509_free(cert); + foundCert = true; + } + BIO_free(bio); + if (!foundCert) { + throw IoTDBException("No valid certificate found in PEM store: " + path); + } +} + +#endif // WITH_SSL + +} // namespace + +std::string SslConfig::effectiveTrustStore() const { + if (hasText(trustStore)) { + return trimToEmpty(trustStore); + } + return trimToEmpty(trustCertFilePath); +} + +void RpcSslUtils::configure(const std::string& sslProtocol) { + gDefaultProtocol = normalizeProtocol(sslProtocol); +} + +std::string RpcSslUtils::getProtocol() { + return gDefaultProtocol; +} + +bool RpcSslUtils::isTlcpProtocol(const std::string& protocol) { + return toUpper(trimToEmpty(protocol)).find("TLCP") == 0; +} + +std::string RpcSslUtils::normalizeProtocol(const std::string& value) { + const std::string trimmed = trimToEmpty(value); + return trimmed.empty() ? DEFAULT_PROTOCOL : trimmed; +} + +std::string RpcSslUtils::resolveProtocol(const std::string& value) { + const std::string trimmed = trimToEmpty(value); + return trimmed.empty() ? gDefaultProtocol : trimmed; +} + +void RpcSslUtils::validateTrustStore(const std::string& trustStorePath, + const std::string& trustStorePassword) { +#if WITH_SSL + ensureFileReadable(trustStorePath, "Trust store"); + if (isPkcs12Path(trustStorePath)) { + validatePkcs12Store(trustStorePath, trustStorePassword); + } else { + validatePemStore(trustStorePath); + } +#else + (void)trustStorePath; + (void)trustStorePassword; + throw IoTDBException("SSL/TLS support is not enabled in this build."); +#endif +} + +void RpcSslUtils::validateKeyStore(const std::string& keyStorePath, + const std::string& keyStorePassword) { +#if WITH_SSL + ensureFileReadable(keyStorePath, "Key store"); + if (isPkcs12Path(keyStorePath)) { + validatePkcs12Store(keyStorePath, keyStorePassword); + } else { + validatePemStore(keyStorePath); + } +#else + (void)keyStorePath; + (void)keyStorePassword; + throw IoTDBException("SSL/TLS support is not enabled in this build."); +#endif +} + +#if WITH_SSL + +SSL_CTX* RpcSslUtils::createClientSslContext(const SslConfig& config) { + const std::string protocol = resolveProtocol(config.sslProtocol); + if (isTlcpProtocol(protocol)) { + return createTlcpClientContext(config); + } + return createTlsClientContext(config); +} + +std::shared_ptr +RpcSslUtils::createSslSocketFactory(const SslConfig& config) { + auto sslConfig = std::make_shared(config); + auto factory = std::make_shared( + [sslConfig]() -> std::shared_ptr { + SSL_CTX* ctx = createClientSslContext(*sslConfig); + return std::make_shared(ctx); + }); + factory->authenticate(false); + return factory; +} + +#endif diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h new file mode 100644 index 000000000000..f164892be928 --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_RPC_SSL_UTILS_H +#define IOTDB_RPC_SSL_UTILS_H + +#include +#include + +#if WITH_SSL +#include +#include +#endif + +struct SslConfig { + bool useSsl = false; + std::string sslProtocol = "TLS"; + std::string trustStore; + std::string trustStorePwd; + std::string keyStore; + std::string keyStorePwd; + /** Legacy PEM trust certificate path; used when trustStore is empty. */ + std::string trustCertFilePath; + + std::string effectiveTrustStore() const; +}; + +class RpcSslUtils { +public: + static constexpr const char* DEFAULT_PROTOCOL = "TLS"; + static constexpr const char* DEFAULT_TLCP_CIPHER = "ECC-SM2-WITH-SM4-SM3"; + + static void configure(const std::string& sslProtocol); + static std::string getProtocol(); + + static bool isTlcpProtocol(const std::string& protocol); + static std::string normalizeProtocol(const std::string& value); + static std::string resolveProtocol(const std::string& value); + + static void validateTrustStore(const std::string& trustStorePath, + const std::string& trustStorePassword); + static void validateKeyStore(const std::string& keyStorePath, + const std::string& keyStorePassword); + +#if WITH_SSL + static SSL_CTX* createClientSslContext(const SslConfig& config); + static std::shared_ptr + createSslSocketFactory(const SslConfig& config); +#endif +}; + +#endif // IOTDB_RPC_SSL_UTILS_H diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp index dfdb0198e387..d9a78b06c1ad 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp @@ -18,6 +18,7 @@ */ #include "SessionConnection.h" #include "SessionImpl.h" +#include "RpcSslUtils.h" #include "RpcCommon.h" #include "common_types.h" #include @@ -46,7 +47,7 @@ SessionConnection::SessionConnection(Session::Impl* session_ptr, const TEndPoint sqlDialect(std::move(dialect)), database(std::move(db)) { this->zoneId = zoneId.empty() ? getSystemDefaultZoneId() : zoneId; endPointList.push_back(endpoint); - init(endPoint, session->useSSL_, session->trustCertFilePath_); + init(endPoint, session->sslConfig_); } void SessionConnection::close() { @@ -92,12 +93,10 @@ SessionConnection::~SessionConnection() { } } -void SessionConnection::init(const TEndPoint& endpoint, bool useSSL, - const std::string& trustCertFilePath) { - if (useSSL) { +void SessionConnection::init(const TEndPoint& endpoint, const SslConfig& sslConfig) { + if (sslConfig.useSsl) { #if WITH_SSL - socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str()); - socketFactory_->authenticate(false); + socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint.ip, endPoint.port); sslSocket->setConnTimeout(connectionTimeoutInMs); transport = std::make_shared(sslSocket); @@ -332,7 +331,7 @@ bool SessionConnection::reconnect() { } tryHostNum++; try { - init(this->endPoint, this->session->useSSL_, this->session->trustCertFilePath_); + init(this->endPoint, this->session->sslConfig_); reconnect = true; } catch (const IoTDBConnectionException& e) { log_warn("The current node may have been down, connection exception: %s", e.what()); diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.h b/iotdb-client/client-cpp/src/rpc/SessionConnection.h index 472e29fd6654..5216c96bd803 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.h +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.h @@ -53,7 +53,7 @@ class SessionConnection : public std::enable_shared_from_this const TEndPoint& getEndPoint(); - void init(const TEndPoint& endpoint, bool useSSL, const std::string& trustCertFilePath); + void init(const TEndPoint& endpoint, const SslConfig& sslConfig); void insertStringRecord(const TSInsertStringRecordReq& request); diff --git a/iotdb-client/client-cpp/src/rpc/SessionImpl.h b/iotdb-client/client-cpp/src/rpc/SessionImpl.h index 9fc3d9172934..b07f01b00869 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionImpl.h +++ b/iotdb-client/client-cpp/src/rpc/SessionImpl.h @@ -31,6 +31,7 @@ #include "DeviceID.h" #include "Endpoint.h" #include "NodesSupplier.h" +#include "RpcSslUtils.h" #include "Session.h" #include "SessionConnection.h" #include "ThriftConvert.h" @@ -41,8 +42,7 @@ class Session::Impl { public: std::string host_; int rpcPort_ = 6667; - bool useSSL_ = false; - std::string trustCertFilePath_; + SslConfig sslConfig_; std::vector nodeUrls_; std::string username_ = "root"; std::string password_ = "root"; diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp index 1cc6c5417b2d..c2a173865d34 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp @@ -17,6 +17,7 @@ * under the License. */ #include "ThriftConnection.h" +#include "RpcSslUtils.h" #include #include #include @@ -64,13 +65,11 @@ void ThriftConnection::initZoneId() { } void ThriftConnection::init(const std::string& username, const std::string& password, - bool enableRPCCompression, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, - const std::string& version) { - if (useSSL) { + bool enableRPCCompression, const SslConfig& sslConfig, + const std::string& zoneId, const std::string& version) { + if (sslConfig.useSsl) { #if WITH_SSL - socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str()); - socketFactory_->authenticate(false); + socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint_.ip, endPoint_.port); sslSocket->setConnTimeout(connectionTimeoutInMs_); transport_ = std::make_shared(sslSocket); diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h index 286911740316..495b74d77dd8 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h @@ -24,6 +24,7 @@ #include #endif #include "IClientRPCService.h" +#include "RpcSslUtils.h" #include "SessionConfig.h" class SessionDataSet; @@ -43,9 +44,8 @@ class ThriftConnection { ~ThriftConnection(); void init(const std::string& username, const std::string& password, - bool enableRPCCompression = false, bool useSSL = false, - const std::string& trustCertFilePath = "", const std::string& zoneId = std::string(), - const std::string& version = "V_1_0"); + bool enableRPCCompression = false, const SslConfig& sslConfig = SslConfig(), + const std::string& zoneId = std::string(), const std::string& version = "V_1_0"); std::unique_ptr executeQueryStatement(const std::string& sql, int64_t timeoutInMs = -1); diff --git a/iotdb-client/client-cpp/src/session/Session.cpp b/iotdb-client/client-cpp/src/session/Session.cpp index 7dd0a7813ed5..1d9e5f7644bd 100644 --- a/iotdb-client/client-cpp/src/session/Session.cpp +++ b/iotdb-client/client-cpp/src/session/Session.cpp @@ -27,6 +27,7 @@ #include #include #include "SessionImpl.h" +#include "RpcSslUtils.h" #include "SessionDataSet.h" #include "ThriftConvert.h" @@ -541,8 +542,13 @@ Session::Session(AbstractSessionBuilder* builder) : impl_(new Impl()) { impl_->enableRedirection_ = builder->enableRedirections; impl_->connectTimeoutMs_ = builder->connectTimeoutMs; impl_->nodeUrls_ = builder->nodeUrls; - impl_->useSSL_ = builder->useSSL; - impl_->trustCertFilePath_ = builder->trustCertFilePath; + impl_->sslConfig_.useSsl = builder->useSSL; + impl_->sslConfig_.sslProtocol = builder->sslProtocol; + impl_->sslConfig_.trustStore = builder->trustStore; + impl_->sslConfig_.trustStorePwd = builder->trustStorePwd; + impl_->sslConfig_.keyStore = builder->keyStore; + impl_->sslConfig_.keyStorePwd = builder->keyStorePwd; + impl_->sslConfig_.trustCertFilePath = builder->trustCertFilePath; impl_->initZoneId(); impl_->initNodesSupplier(impl_->nodeUrls_); } @@ -555,6 +561,13 @@ void Session::setDatabase(const std::string& database) { impl_->database_ = database; } +void Session::setSslConfig(const SslConfig& sslConfig) { + if (!impl_->isClosed_) { + throw IoTDBException("Cannot change SSL configuration after Session is opened."); + } + impl_->sslConfig_ = sslConfig; +} + std::string Session::getDatabase() { return impl_->database_; } @@ -871,7 +884,7 @@ void Session::Impl::initNodesSupplier(const std::vector& nodeUrls) if (enableAutoFetch_) { nodesSupplier_ = - NodesSupplier::create(endPoints, username_, password_, useSSL_, trustCertFilePath_); + NodesSupplier::create(endPoints, username_, password_, sslConfig_); } else { nodesSupplier_ = make_shared(endPoints); } diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp b/iotdb-client/client-cpp/src/session/SessionC.cpp index 79287cf02523..5cb2d978eea1 100644 --- a/iotdb-client/client-cpp/src/session/SessionC.cpp +++ b/iotdb-client/client-cpp/src/session/SessionC.cpp @@ -24,6 +24,7 @@ #include "TableSessionBuilder.h" #include "SessionBuilder.h" #include "SessionDataSet.h" +#include "RpcSslUtils.h" #include #include @@ -39,10 +40,14 @@ struct CSession_ { std::shared_ptr cpp; + SslConfig sslConfig; + bool sslConfigured = false; }; struct CTableSession_ { std::shared_ptr cpp; + SslConfig sslConfig; + bool sslConfigured = false; }; struct CTablet_ { @@ -154,6 +159,32 @@ static std::map toStringMap(int count, const char* con return m; } +static void applyPendingSslConfig(CSession* session) { + if (session != nullptr && session->sslConfigured) { + session->cpp->setSslConfig(session->sslConfig); + } +} + +static void applyPendingSslConfig(CTableSession* session) { + if (session != nullptr && session->sslConfigured) { + session->cpp->setSslConfig(session->sslConfig); + } +} + +static TsStatus setSslStringField(std::string& field, const char* value, const char* label) { + if (value == nullptr) { + return setError(TS_ERR_INVALID_PARAM, std::string(label) + " is null"); + } + field = value; + return TS_OK; +} + +static std::shared_ptr createTableSession(TableSessionBuilder* builder) { + builder->sqlDialect = "table"; + auto session = std::make_shared(builder); + return std::make_shared(session); +} + /** * Convert C typed values (void* const* values, TSDataType_C* types, int count) * to C++ vector that Session expects. @@ -301,6 +332,7 @@ TsStatus ts_session_open(CSession* session) { if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(); return TS_OK; } catch (const std::exception& e) { @@ -313,6 +345,7 @@ TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompr if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(enableRPCCompression); return TS_OK; } catch (const std::exception& e) { @@ -332,6 +365,70 @@ TsStatus ts_session_close(CSession* session) { } } +TsStatus ts_session_set_use_ssl(CSession* session, bool useSsl) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + session->sslConfig.useSsl = useSsl; + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_ssl_protocol(CSession* session, const char* sslProtocol) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.sslProtocol, sslProtocol, "sslProtocol"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + +TsStatus ts_session_set_trust_store(CSession* session, const char* trustStore, + const char* trustStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustStore, trustStore, "trustStore"); + if (status != TS_OK) { + return status; + } + if (trustStorePwd != nullptr) { + session->sslConfig.trustStorePwd = trustStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, + const char* keyStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.keyStore, keyStore, "keyStore"); + if (status != TS_OK) { + return status; + } + if (keyStorePwd != nullptr) { + session->sslConfig.keyStorePwd = keyStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = + setSslStringField(session->sslConfig.trustCertFilePath, trustCertFilePath, "trustCertFilePath"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + /* ============================================================ * Session Lifecycle — Table Model * ============================================================ */ @@ -340,17 +437,14 @@ CTableSession* ts_table_session_new(const char* host, int rpcPort, const char* u const char* password, const char* database) { clearError(); try { - std::unique_ptr builder(new TableSessionBuilder()); - auto tableSession = builder->host(std::string(host)) - ->rpcPort(rpcPort) - ->username(std::string(username)) - ->password(std::string(password)) - ->database(std::string(database ? database : "")) - ->build(); - CTableSession_ tmp{}; - tmp.cpp = std::move(tableSession); + TableSessionBuilder builder; + builder.host(std::string(host)) + ->rpcPort(rpcPort) + ->username(std::string(username)) + ->password(std::string(password)) + ->database(std::string(database ? database : "")); auto* cts = new CTableSession_(); - cts->cpp = std::move(tmp.cpp); + cts->cpp = createTableSession(&builder); return cts; } catch (const std::exception& e) { handleException(e); @@ -364,16 +458,13 @@ CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int clearError(); try { auto urls = toStringVec(nodeUrls, urlCount); - std::unique_ptr builder(new TableSessionBuilder()); - auto tableSession = builder->nodeUrls(urls) - ->username(std::string(username)) - ->password(std::string(password)) - ->database(std::string(database ? database : "")) - ->build(); - CTableSession_ tmp{}; - tmp.cpp = std::move(tableSession); + TableSessionBuilder builder; + builder.nodeUrls(urls) + ->username(std::string(username)) + ->password(std::string(password)) + ->database(std::string(database ? database : "")); auto* cts = new CTableSession_(); - cts->cpp = std::move(tmp.cpp); + cts->cpp = createTableSession(&builder); return cts; } catch (const std::exception& e) { handleException(e); @@ -390,6 +481,7 @@ TsStatus ts_table_session_open(CTableSession* session) { if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(); return TS_OK; } catch (const std::exception& e) { @@ -409,6 +501,71 @@ TsStatus ts_table_session_close(CTableSession* session) { } } +TsStatus ts_table_session_set_use_ssl(CTableSession* session, bool useSsl) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + session->sslConfig.useSsl = useSsl; + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_ssl_protocol(CTableSession* session, const char* sslProtocol) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.sslProtocol, sslProtocol, "sslProtocol"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + +TsStatus ts_table_session_set_trust_store(CTableSession* session, const char* trustStore, + const char* trustStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustStore, trustStore, "trustStore"); + if (status != TS_OK) { + return status; + } + if (trustStorePwd != nullptr) { + session->sslConfig.trustStorePwd = trustStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyStore, + const char* keyStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.keyStore, keyStore, "keyStore"); + if (status != TS_OK) { + return status; + } + if (keyStorePwd != nullptr) { + session->sslConfig.keyStorePwd = keyStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, + const char* trustCertFilePath) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = + setSslStringField(session->sslConfig.trustCertFilePath, trustCertFilePath, "trustCertFilePath"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + /* ============================================================ * Timezone * ============================================================ */ diff --git a/iotdb-client/client-cpp/src/session/SessionPool.cpp b/iotdb-client/client-cpp/src/session/SessionPool.cpp index a828f0ac2c6d..42961dbaff61 100644 --- a/iotdb-client/client-cpp/src/session/SessionPool.cpp +++ b/iotdb-client/client-cpp/src/session/SessionPool.cpp @@ -109,6 +109,31 @@ SessionPool& SessionPool::setTrustCertFilePath(std::string path) { return *this; } +SessionPool& SessionPool::setSslProtocol(std::string sslProtocol) { + sslProtocol_ = std::move(sslProtocol); + return *this; +} + +SessionPool& SessionPool::setTrustStore(std::string trustStore) { + trustStore_ = std::move(trustStore); + return *this; +} + +SessionPool& SessionPool::setTrustStorePwd(std::string trustStorePwd) { + trustStorePwd_ = std::move(trustStorePwd); + return *this; +} + +SessionPool& SessionPool::setKeyStore(std::string keyStore) { + keyStore_ = std::move(keyStore); + return *this; +} + +SessionPool& SessionPool::setKeyStorePwd(std::string keyStorePwd) { + keyStorePwd_ = std::move(keyStorePwd); + return *this; +} + std::shared_ptr SessionPool::constructNewSession() { AbstractSessionBuilder builder; builder.host = host_; @@ -126,6 +151,11 @@ std::shared_ptr SessionPool::constructNewSession() { builder.connectTimeoutMs = connectTimeoutMs_; builder.useSSL = useSSL_; builder.trustCertFilePath = trustCertFilePath_; + builder.sslProtocol = sslProtocol_; + builder.trustStore = trustStore_; + builder.trustStorePwd = trustStorePwd_; + builder.keyStore = keyStore_; + builder.keyStorePwd = keyStorePwd_; auto session = std::make_shared(&builder); session->open(enableRPCCompression_, connectTimeoutMs_); diff --git a/iotdb-client/client-cpp/src/session/TableSession.cpp b/iotdb-client/client-cpp/src/session/TableSession.cpp index 9cd80b7dd789..4c7fc9b5edb1 100644 --- a/iotdb-client/client-cpp/src/session/TableSession.cpp +++ b/iotdb-client/client-cpp/src/session/TableSession.cpp @@ -20,6 +20,7 @@ // This file is a translation of the Java file iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java #include "TableSession.h" +#include "RpcSslUtils.h" #include "SessionDataSet.h" void TableSession::insert(Tablet& tablet, bool sorted) { @@ -43,4 +44,8 @@ void TableSession::open(bool enableRPCCompression) { } void TableSession::close() { session_->close(); +} + +void TableSession::setSslConfig(const SslConfig& sslConfig) { + session_->setSslConfig(sslConfig); } \ No newline at end of file diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index 9d5428edc4b8..15c5991ff8e3 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -42,12 +42,18 @@ set(_test_targets session_tests session_relational_tests session_c_tests - session_c_relational_tests) + session_c_relational_tests + rpc_ssl_utils_tests) add_executable(session_tests main.cpp cpp/sessionIT.cpp) add_executable(session_relational_tests main_Relational.cpp cpp/sessionRelationalIT.cpp) add_executable(session_c_tests main_c.cpp cpp/sessionCIT.cpp) add_executable(session_c_relational_tests main_c_Relational.cpp cpp/sessionCRelationalIT.cpp) +add_executable(rpc_ssl_utils_tests + main_rpc_ssl.cpp + cpp/RpcSslUtilsTest.cpp + cpp/RpcSslMutualAuthTest.cpp + cpp/SslTestFixtures.cpp) foreach(_t IN LISTS _test_targets) target_include_directories(${_t} PRIVATE @@ -64,6 +70,31 @@ foreach(_t IN LISTS _test_targets) endif() endforeach() +if(WITH_SSL) + if(WIN32) + set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl.exe") + else() + set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl") + endif() + file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" _iotdb_test_fixtures_dir) + file(TO_CMAKE_PATH "${_iotdb_openssl_executable}" _iotdb_openssl_executable_cmake) + string(REPLACE "\\" "/" _iotdb_test_fixtures_dir_fwd "${_iotdb_test_fixtures_dir}") + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + target_compile_definitions(rpc_ssl_utils_tests PRIVATE + IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir_fwd}" + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + add_custom_command(TARGET rpc_ssl_utils_tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" + "$/fixtures" + COMMENT "Copy SSL test fixtures next to rpc_ssl_utils_tests") + if(WIN32) + target_link_libraries(rpc_ssl_utils_tests PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + else() + target_link_libraries(rpc_ssl_utils_tests PRIVATE iotdb_thrift_static) + endif() +endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) foreach(_t IN LISTS _test_targets) target_compile_options(${_t} PRIVATE -fsanitize=address -fno-omit-frame-pointer) @@ -86,19 +117,30 @@ if(MSVC) add_test(NAME sessionRelationalIT CONFIGURATIONS Release COMMAND session_relational_tests) add_test(NAME sessionCIT CONFIGURATIONS Release COMMAND session_c_tests) add_test(NAME sessionCRelationalIT CONFIGURATIONS Release COMMAND session_c_relational_tests) + add_test(NAME rpcSslUtilsTest CONFIGURATIONS Release COMMAND rpc_ssl_utils_tests) foreach(_t IN LISTS _test_targets) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + if(WITH_SSL) + _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls) + foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_dll}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endforeach() + endif() endforeach() else() add_test(NAME sessionIT COMMAND session_tests) add_test(NAME sessionRelationalIT COMMAND session_relational_tests) add_test(NAME sessionCIT COMMAND session_c_tests) add_test(NAME sessionCRelationalIT COMMAND session_c_relational_tests) + add_test(NAME rpcSslUtilsTest COMMAND rpc_ssl_utils_tests) endif() # Run sequentially: parallel ctest overloads the single local IoTDB instance. set_tests_properties( - sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT + sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT rpcSslUtilsTest PROPERTIES RUN_SERIAL TRUE) diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp new file mode 100644 index 000000000000..53e7f70ea81f --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp @@ -0,0 +1,193 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +TEST_CASE("TLS mutual auth creates client SSL_CTX with trust and key stores", "[rpc][ssl][mutual]") { +#if WITH_SSL + const std::string trustStore = ssltest::tlsFixture("tls-trust.p12"); + const std::string keyStore = ssltest::tlsFixture("tls-client.p12"); + REQUIRE(fixtureExists(trustStore)); + REQUIRE(fixtureExists(keyStore)); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = trustStore; + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); + REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + REQUIRE(ssltest::sslContextHasClientCertificate(ctx)); + SSL_CTX_free(ctx); +#endif +} + +TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl][mutual]") { +#if WITH_SSL + const std::string trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + REQUIRE(fixtureExists(trustStore)); + const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); + REQUIRE_FALSE(keyStore.empty()); + REQUIRE(fixtureExists(keyStore)); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = trustStore; + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); + REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); +#endif +} + +TEST_CASE("TLS mutual auth handshake with openssl s_server", "[rpc][ssl][mutual][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlsFixture("ca.crt"); + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(caFile)); + REQUIRE(fixtureExists(serverCert)); + REQUIRE(fixtureExists(serverKey)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-tls1_2", + "-Verify", "1", + "-CAfile", caFile, + "-cert", serverCert, + "-key", serverKey, + "-www", + }); + REQUIRE(started); + REQUIRE(server.running()); + REQUIRE(server.port() > 0); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = ssltest::tlsFixture("tls-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = ssltest::tlsFixture("tls-client.p12"); + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLS one-way auth fails when server requires client certificate", "[rpc][ssl][mutual][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlsFixture("ca.crt"); + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(caFile)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-tls1_2", + "-Verify", "1", + "-CAfile", caFile, + "-cert", serverCert, + "-key", serverKey, + "-www", + }); + REQUIRE(started); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = ssltest::tlsFixture("tls-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + + REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLCP mutual auth handshake with openssl NTLS s_server", "[rpc][ssl][mutual][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlcpFixture("ca.crt"); + const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); + const std::string signKey = ssltest::tlcpFixture("server_sign.key"); + const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); + const std::string encKey = ssltest::tlcpFixture("server_enc.key"); + REQUIRE(fixtureExists(caFile)); + REQUIRE(fixtureExists(signCert)); + REQUIRE(fixtureExists(signKey)); + REQUIRE(fixtureExists(encCert)); + REQUIRE(fixtureExists(encKey)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-enable_ntls", + "-ntls", + "-Verify", "1", + "-CAfile", caFile, + "-sign_cert", signCert, + "-sign_key", signKey, + "-enc_cert", encCert, + "-enc_key", encKey, + "-www", + }); + REQUIRE(started); + REQUIRE(server.running()); + + const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); + REQUIRE_FALSE(keyStore.empty()); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp new file mode 100644 index 000000000000..54b48e67667e --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include + +#include "Common.h" +#include "RpcSslUtils.h" + +TEST_CASE("RpcSslUtils protocol helpers", "[rpc][ssl]") { + REQUIRE(RpcSslUtils::normalizeProtocol("") == "TLS"); + REQUIRE(RpcSslUtils::normalizeProtocol(" TLSv1.3 ") == "TLSv1.3"); + REQUIRE(RpcSslUtils::isTlcpProtocol("TLCP") == true); + REQUIRE(RpcSslUtils::isTlcpProtocol(" tlcp1.1 ") == true); + REQUIRE(RpcSslUtils::isTlcpProtocol("TLS") == false); + + const std::string origin = RpcSslUtils::getProtocol(); + RpcSslUtils::configure("ConfiguredProtocol"); + REQUIRE(RpcSslUtils::resolveProtocol("") == "ConfiguredProtocol"); + REQUIRE(RpcSslUtils::resolveProtocol(" ExplicitProtocol ") == "ExplicitProtocol"); + RpcSslUtils::configure(origin); +} + +TEST_CASE("SslConfig effectiveTrustStore backward compatibility", "[rpc][ssl]") { + SslConfig config; + config.trustStore = "/path/to/trust.p12"; + config.trustCertFilePath = "/legacy/ca.pem"; + REQUIRE(config.effectiveTrustStore() == "/path/to/trust.p12"); + + config.trustStore.clear(); + config.trustCertFilePath = "/legacy/ca.pem"; + REQUIRE(config.effectiveTrustStore() == "/legacy/ca.pem"); +} + +TEST_CASE("RpcSslUtils store validation rejects missing files", "[rpc][ssl]") { + REQUIRE_THROWS_AS(RpcSslUtils::validateTrustStore("/path/does/not/exist.pem", ""), + IoTDBException); + REQUIRE_THROWS_AS(RpcSslUtils::validateKeyStore("/path/does/not/exist.p12", "pwd"), + IoTDBException); +} + +#if WITH_SSL +TEST_CASE("RpcSslUtils createClientSslContext for TLS without trust store", "[rpc][ssl]") { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); +} +#endif diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp new file mode 100644 index 000000000000..0cb2fbe45110 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -0,0 +1,644 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#if WITH_SSL +#include +#include +#include +#include +#include +#endif + +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ssltest { +namespace { + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string configuredOrEmpty() { +#ifdef IOTDB_TEST_FIXTURES_DIR + return IOTDB_TEST_FIXTURES_DIR; +#else + return joinPath(executableDir(), "fixtures"); +#endif +} + +std::string firstExistingRoot() { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + if (pathExists(joinPath(configured, "tls/tls-trust.p12")) || + pathExists(joinPath(configured, "tls\\tls-trust.p12"))) { + return configured; + } +#endif + const std::string copied = joinPath(executableDir(), "fixtures"); + if (pathExists(joinPath(copied, "tls/tls-trust.p12")) || + pathExists(joinPath(copied, "tls\\tls-trust.p12"))) { + return copied; + } + return configuredOrEmpty(); +} + +#if WITH_SSL +EVP_PKEY* readPrivateKeyPem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return key; +} + +X509* readCertificatePem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return cert; +} + +void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { + unsigned char keyid[EVP_MAX_MD_SIZE]; + unsigned int keyidLen = 0; + if (X509_pubkey_digest(cert, EVP_sha1(), keyid, &keyidLen) == 1) { + PKCS12_add_localkeyid(bag, keyid, static_cast(keyidLen)); + } +} + +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, + const char* friendlyName, const std::string& password) { + PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); + PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); + addLocalKeyId(certbag, cert); + sk_PKCS12_SAFEBAG_push(bags, certbag); + + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + if (p8 == nullptr) { + return; + } + PKCS12_SAFEBAG* keybag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (keybag == nullptr) { + return; + } + PKCS12_add_friendlyname_utf8(keybag, friendlyName, -1); + addLocalKeyId(keybag, cert); + sk_PKCS12_SAFEBAG_push(bags, keybag); +} + +bool writePkcs12File(PKCS12* p12, const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "wb"); + if (bio == nullptr) { + return false; + } + const int rc = i2d_PKCS12_bio(bio, p12); + BIO_free(bio); + return rc == 1; +} + +PKCS12* readPkcs12File(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + PKCS12* p12 = d2i_PKCS12_bio(bio, nullptr); + BIO_free(bio); + return p12; +} + +void forEachPkcs12Bag(PKCS12* p12, const std::string& password, + const std::function& visitor) { + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(p12); + if (safes == nullptr) { + return; + } + for (int i = 0; i < sk_PKCS7_num(safes); ++i) { + PKCS7* p7 = sk_PKCS7_value(safes, i); + STACK_OF(PKCS12_SAFEBAG)* bags = nullptr; + if (PKCS7_type_is_data(p7)) { + bags = PKCS12_unpack_p7data(p7); + } else if (PKCS7_type_is_encrypted(p7)) { + bags = PKCS12_unpack_p7encdata(p7, password.c_str(), static_cast(password.size())); + } + if (bags == nullptr) { + continue; + } + for (int j = 0; j < sk_PKCS12_SAFEBAG_num(bags); ++j) { + visitor(sk_PKCS12_SAFEBAG_value(bags, j)); + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + sk_PKCS7_pop_free(safes, PKCS7_free); +} + +void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, + const std::string& password) { + forEachPkcs12Bag(source, password, [&](PKCS12_SAFEBAG* bag) { + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + char* friendlyName = PKCS12_get_friendlyname(bag); + if (bagType == NID_certBag) { + X509* cert = PKCS12_certbag2x509(bag); + if (cert != nullptr) { + PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_cert(cert); + if (friendlyName != nullptr) { + PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); + } + sk_PKCS12_SAFEBAG_push(target, newBag); + X509_free(cert); + } + } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { + EVP_PKEY* key = nullptr; + if (bagType == NID_pkcs8ShroudedKeyBag) { + PKCS8_PRIV_KEY_INFO* p8 = PKCS12_decrypt_skey(bag, password.c_str(), + static_cast(password.size())); + if (p8 != nullptr) { + key = EVP_PKCS82PKEY(p8); + PKCS8_PRIV_KEY_INFO_free(p8); + } + } else { + const PKCS8_PRIV_KEY_INFO* p8 = PKCS12_SAFEBAG_get0_p8inf(bag); + if (p8 != nullptr) { + key = EVP_PKCS82PKEY(p8); + } + } + if (key != nullptr) { + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + EVP_PKEY_free(key); + if (p8 != nullptr) { + PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, + p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (newBag != nullptr) { + if (friendlyName != nullptr) { + PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); + } + sk_PKCS12_SAFEBAG_push(target, newBag); + } + } + } + } + if (friendlyName != nullptr) { + OPENSSL_free(friendlyName); + } + }); +} + +std::string opensslExecutable() { +#ifdef IOTDB_OPENSSL_EXECUTABLE + return IOTDB_OPENSSL_EXECUTABLE; +#else + return "openssl"; +#endif +} + +std::string quoteArg(const std::string& arg) { +#if defined(_WIN32) + return "\"" + arg + "\""; +#else + if (arg.find(' ') != std::string::npos) { + return "\"" + arg + "\""; + } + return arg; +#endif +} + +} // namespace + +std::string fixturesRoot() { + return firstExistingRoot(); +} + +std::string tlsFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tls"), name); +} + +std::string tlcpFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tlcp"), name); +} + +std::string buildTlcpDualKeyStoreP12() { + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, nullptr); + const std::string password = kStorePassword; + const std::string outPath = joinPath(executableDir(), "tlcp-client-dual.p12"); + + PKCS12* signStore = readPkcs12File(tlcpFixture("tlcp-client-sign.p12")); + PKCS12* encStore = readPkcs12File(tlcpFixture("tlcp-client-enc.p12")); + if (signStore == nullptr || encStore == nullptr) { + if (signStore != nullptr) { + PKCS12_free(signStore); + } + if (encStore != nullptr) { + PKCS12_free(encStore); + } + return ""; + } + + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(signStore); + STACK_OF(PKCS7)* encSafes = PKCS12_unpack_authsafes(encStore); + PKCS12_free(signStore); + PKCS12_free(encStore); + if (safes == nullptr || encSafes == nullptr) { + if (safes != nullptr) { + sk_PKCS7_pop_free(safes, PKCS7_free); + } + if (encSafes != nullptr) { + sk_PKCS7_pop_free(encSafes, PKCS7_free); + } + return ""; + } + for (int i = 0; i < sk_PKCS7_num(encSafes); ++i) { + sk_PKCS7_push(safes, sk_PKCS7_value(encSafes, i)); + } + sk_PKCS7_free(encSafes); + + PKCS12* p12 = PKCS12_init(NID_pkcs7_data); + if (PKCS12_pack_authsafes(p12, safes) != 1) { + sk_PKCS7_pop_free(safes, PKCS7_free); + PKCS12_free(p12); + return ""; + } + sk_PKCS7_free(safes); + + const bool written = writePkcs12File(p12, outPath); + PKCS12_free(p12); + (void)password; + return written ? outPath : ""; +} + +bool sslContextHasClientCertificate(SSL_CTX* ctx) { + if (ctx == nullptr) { + return false; + } + X509* cert = SSL_CTX_get0_certificate(ctx); + EVP_PKEY* key = SSL_CTX_get0_privatekey(ctx); + return cert != nullptr && key != nullptr; +} + +bool tlcpContextHasDualCredentials(SSL_CTX* ctx) { + if (ctx == nullptr) { + return false; + } + SSL* ssl = SSL_new(ctx); + if (ssl == nullptr) { + return false; + } + SSL_enable_ntls(ssl); + X509* signCert = SSL_get_sign_certificate_ntls(ssl); + X509* encCert = SSL_get_enc_certificate_ntls(ssl); + const bool ok = signCert != nullptr && encCert != nullptr; + SSL_free(ssl); + return ok; +} + +bool tlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs) { +#if defined(_WIN32) + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif + for (int attempt = 0; attempt < 3; ++attempt) { + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + if (ctx == nullptr) { + continue; + } + SSL* ssl = SSL_new(ctx); + if (ssl == nullptr) { + SSL_CTX_free(ctx); + continue; + } + if (RpcSslUtils::isTlcpProtocol(config.sslProtocol)) { + SSL_enable_ntls(ssl); + } + const std::string target = host + ":" + std::to_string(port); + BIO* bio = BIO_new_connect(target.c_str()); + if (bio == nullptr) { + SSL_free(ssl); + SSL_CTX_free(ctx); + continue; + } + BIO_set_conn_hostname(bio, host.c_str()); + if (BIO_do_connect(bio) <= 0) { + BIO_free_all(bio); + SSL_free(ssl); + SSL_CTX_free(ctx); + continue; + } + SSL_set_bio(ssl, bio, bio); + const int rc = SSL_connect(ssl); + const bool ok = rc == 1; + if (ok) { + SSL_shutdown(ssl); + } + SSL_free(ssl); + SSL_CTX_free(ctx); + if (ok) { +#if defined(_WIN32) + WSACleanup(); +#endif + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } +#if defined(_WIN32) + WSACleanup(); +#endif + (void)timeoutMs; + return false; +} + +int findFreeTcpPort() { +#if defined(_WIN32) + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + return 0; + } +#endif + const int fd = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + if (fd < 0) { +#if defined(_WIN32) + WSACleanup(); +#endif + return 0; + } + sockaddr_in addr {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + if (bind(fd, reinterpret_cast(&addr), sizeof(addr)) != 0) { +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return 0; + } + socklen_t len = sizeof(addr); + if (getsockname(fd, reinterpret_cast(&addr), &len) != 0) { +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return 0; + } + const int port = ntohs(addr.sin_port); +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return port; +} + +OpenSslServerProcess::OpenSslServerProcess() = default; + +OpenSslServerProcess::~OpenSslServerProcess() { + stop(); +} + +bool OpenSslServerProcess::start(const std::vector& args) { + stop(); + port_ = findFreeTcpPort(); + if (port_ <= 0) { + return false; + } + + const std::string portArg = std::to_string(port_); + const std::string exe = opensslExecutable(); + std::vector argStorage; + argStorage.reserve(args.size() + 5); + argStorage.push_back(exe); + argStorage.emplace_back("s_server"); + argStorage.emplace_back("-accept"); + argStorage.emplace_back(portArg); + for (const std::string& arg : args) { + argStorage.push_back(arg); + } + argStorage.emplace_back("-quiet"); + + std::vector argv; + argv.reserve(argStorage.size() + 1); + for (const std::string& arg : argStorage) { + argv.push_back(arg.c_str()); + } + argv.push_back(nullptr); + +#if defined(_WIN32) + std::string cmdline = quoteArg(exe); + for (size_t i = 1; i < argStorage.size(); ++i) { + cmdline.push_back(' '); + cmdline.append(quoteArg(argStorage[i])); + } + std::vector mutableCmdline(cmdline.begin(), cmdline.end()); + mutableCmdline.push_back('\0'); + + STARTUPINFOA si {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + PROCESS_INFORMATION pi {}; + if (!CreateProcessA(nullptr, mutableCmdline.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, + nullptr, nullptr, &si, &pi)) { + return false; + } + processHandle_ = pi.hProcess; + processId_ = pi.dwProcessId; + CloseHandle(pi.hThread); +#else + std::ostringstream command; + command << quoteArg(opensslExecutable()); + for (const std::string& arg : argStorage) { + command << ' ' << quoteArg(arg); + } + const pid_t pid = fork(); + if (pid < 0) { + return false; + } + if (pid == 0) { + execl("/bin/sh", "sh", "-c", command.str().c_str(), static_cast(nullptr)); + _exit(127); + } + childPid_ = pid; +#endif + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + return running(); +} + +void OpenSslServerProcess::stop() { +#if defined(_WIN32) + if (processHandle_ != nullptr) { + TerminateProcess(static_cast(processHandle_), 0); + WaitForSingleObject(static_cast(processHandle_), 2000); + CloseHandle(static_cast(processHandle_)); + processHandle_ = nullptr; + processId_ = 0; + } +#else + if (childPid_ > 0) { + kill(childPid_, SIGTERM); + waitpid(childPid_, nullptr, 0); + childPid_ = -1; + } +#endif + port_ = 0; +} + +bool OpenSslServerProcess::running() const { +#if defined(_WIN32) + if (processHandle_ == nullptr) { + return false; + } + DWORD code = STILL_ACTIVE; + if (!GetExitCodeProcess(static_cast(processHandle_), &code)) { + return false; + } + return code == STILL_ACTIVE; +#else + if (childPid_ <= 0) { + return false; + } + int status = 0; + const pid_t rc = waitpid(childPid_, &status, WNOHANG); + return rc == 0; +#endif +} + +int OpenSslServerProcess::port() const { + return port_; +} + +#else // WITH_SSL + +std::string fixturesRoot() { + return firstExistingRoot(); +} + +std::string tlsFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tls"), name); +} + +std::string tlcpFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tlcp"), name); +} + +std::string buildTlcpDualKeyStoreP12() { + return ""; +} + +int findFreeTcpPort() { + return 0; +} + +OpenSslServerProcess::OpenSslServerProcess() = default; +OpenSslServerProcess::~OpenSslServerProcess() = default; +bool OpenSslServerProcess::start(const std::vector&) { + return false; +} +void OpenSslServerProcess::stop() {} +bool OpenSslServerProcess::running() const { + return false; +} +int OpenSslServerProcess::port() const { + return 0; +} + +#endif // WITH_SSL + +} // namespace ssltest diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h new file mode 100644 index 000000000000..0787e9062aec --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_SSL_TEST_FIXTURES_H +#define IOTDB_SSL_TEST_FIXTURES_H + +#include +#include +#include +#include + +namespace ssltest { + +constexpr const char* kStorePassword = "thrift"; + +/** Root directory containing tls/ and tlcp/ fixture subfolders. */ +std::string fixturesRoot(); + +std::string tlsFixture(const std::string& name); +std::string tlcpFixture(const std::string& name); + +/** Build a TLCP dual-cert PKCS12 key store from PEM fixtures (sign + enc). */ +std::string buildTlcpDualKeyStoreP12(); + +#if WITH_SSL +#include + +bool sslContextHasClientCertificate(SSL_CTX* ctx); +bool tlcpContextHasDualCredentials(SSL_CTX* ctx); + +bool tlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs = 5000); +#endif + +/** Spawn bundled Tongsuo openssl s_server for integration-style handshake tests. */ +class OpenSslServerProcess { +public: + OpenSslServerProcess(); + ~OpenSslServerProcess(); + + OpenSslServerProcess(const OpenSslServerProcess&) = delete; + OpenSslServerProcess& operator=(const OpenSslServerProcess&) = delete; + + bool start(const std::vector& args); + void stop(); + bool running() const; + int port() const; + +private: +#if defined(_WIN32) + void* processHandle_ = nullptr; + unsigned long processId_ = 0; +#else + int childPid_ = -1; +#endif + int port_ = 0; +}; + +int findFreeTcpPort(); + +} // namespace ssltest + +#endif // IOTDB_SSL_TEST_FIXTURES_H diff --git a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp index 4a298dd1c1a1..584fe1d6c8fb 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp @@ -245,8 +245,10 @@ TEST_CASE("C API Table - Multi-node table session", "[c_table_multiNode][c_table CTableSession* localSession = ts_table_session_new_multi_node(urls, 1, "root", "root", ""); REQUIRE(localSession != nullptr); - TsStatus status = - ts_table_session_execute_non_query(localSession, "DROP DATABASE IF EXISTS c_db5"); + TsStatus status = ts_table_session_open(localSession); + REQUIRE(status == TS_OK); + + status = ts_table_session_execute_non_query(localSession, "DROP DATABASE IF EXISTS c_db5"); REQUIRE(status == TS_OK); ts_table_session_execute_non_query(localSession, "CREATE DATABASE c_db5"); diff --git a/iotdb-client/client-cpp/test/fixtures/.gitignore b/iotdb-client/client-cpp/test/fixtures/.gitignore new file mode 100644 index 000000000000..425d89e3c2dc --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/.gitignore @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +*.csr +*.srl +ca.key +tlcp-client-dual.p12 +_gen/ diff --git a/iotdb-client/client-cpp/test/fixtures/README.md b/iotdb-client/client-cpp/test/fixtures/README.md new file mode 100644 index 000000000000..6cd6b14a03f9 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/README.md @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Self-signed TLS/TLCP test certificates for C++ client SSL unit tests only. +# Password for all PKCS12 files: thrift +# +# Regenerate with: +# test/fixtures/generate_fixtures.cmd (Windows) +# test/fixtures/generate_fixtures.sh (Linux/macOS) diff --git a/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd new file mode 100644 index 000000000000..24fd2303df71 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd @@ -0,0 +1,69 @@ +@echo off +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. +REM +REM Regenerate TLS/TLCP PKCS12 and PEM fixtures using the bundled Tongsuo openssl. +REM Usage (from client-cpp/test/fixtures, after cmake build): +REM generate_fixtures.cmd [path\to\openssl.exe] + +@echo off +setlocal enabledelayedexpansion + +set "SCRIPT_DIR=%~dp0" +set "TLS_DIR=%SCRIPT_DIR%tls" +set "TLCP_DIR=%SCRIPT_DIR%tlcp" +set "OPENSSL=%~1" +if "%OPENSSL%"=="" set "OPENSSL=..\..\target\build\_deps\tongsuo\install\bin\openssl.exe" +if not exist "%OPENSSL%" ( + echo OpenSSL executable not found: %OPENSSL% + exit /b 1 +) + +set "PASS=thrift" +mkdir "%TLS_DIR%" 2>nul +mkdir "%TLCP_DIR%" 2>nul + +echo [fixtures] generating TLS RSA fixtures... +"%OPENSSL%" genrsa -out "%TLS_DIR%\ca.key" 2048 +"%OPENSSL%" req -new -x509 -days 3650 -key "%TLS_DIR%\ca.key" -out "%TLS_DIR%\ca.crt" -subj "/CN=IoTDB Test CA" +"%OPENSSL%" genrsa -out "%TLS_DIR%\server.key" 2048 +"%OPENSSL%" req -new -key "%TLS_DIR%\server.key" -out "%TLS_DIR%\server.csr" -subj "/CN=localhost" +"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\server.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\server.crt" +"%OPENSSL%" genrsa -out "%TLS_DIR%\client.key" 2048 +"%OPENSSL%" req -new -key "%TLS_DIR%\client.key" -out "%TLS_DIR%\client.csr" -subj "/CN=IoTDB Test Client" +"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\client.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\client.crt" +"%OPENSSL%" pkcs12 -export -nokeys -in "%TLS_DIR%\ca.crt" -out "%TLS_DIR%\tls-trust.p12" -password pass:%PASS% +"%OPENSSL%" pkcs12 -export -in "%TLS_DIR%\client.crt" -inkey "%TLS_DIR%\client.key" -out "%TLS_DIR%\tls-client.p12" -password pass:%PASS% -name client +"%OPENSSL%" pkcs12 -export -in "%TLS_DIR%\server.crt" -inkey "%TLS_DIR%\server.key" -out "%TLS_DIR%\tls-server.p12" -password pass:%PASS% -name server + +echo [fixtures] generating TLCP SM2 fixtures... +"%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\ca.key" +"%OPENSSL%" req -new -x509 -days 3650 -key "%TLCP_DIR%\ca.key" -out "%TLCP_DIR%\ca.crt" -subj "/CN=IoTDB TLCP CA" -sm3 +for %%R in (client server) do ( + "%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\%%R_sign.key" + "%OPENSSL%" req -new -key "%TLCP_DIR%\%%R_sign.key" -out "%TLCP_DIR%\%%R_sign.csr" -subj "/CN=%%R sign" -sm3 + "%OPENSSL%" x509 -req -days 3650 -in "%TLCP_DIR%\%%R_sign.csr" -CA "%TLCP_DIR%\ca.crt" -CAkey "%TLCP_DIR%\ca.key" -CAcreateserial -out "%TLCP_DIR%\%%R_sign.crt" -sm3 + "%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\%%R_enc.key" + "%OPENSSL%" req -new -key "%TLCP_DIR%\%%R_enc.key" -out "%TLCP_DIR%\%%R_enc.csr" -subj "/CN=%%R enc" -sm3 + "%OPENSSL%" x509 -req -days 3650 -in "%TLCP_DIR%\%%R_enc.csr" -CA "%TLCP_DIR%\ca.crt" -CAkey "%TLCP_DIR%\ca.key" -CAcreateserial -out "%TLCP_DIR%\%%R_enc.crt" -sm3 +) +"%OPENSSL%" pkcs12 -export -nokeys -in "%TLCP_DIR%\ca.crt" -out "%TLCP_DIR%\tlcp-trust.p12" -password pass:%PASS% +"%OPENSSL%" pkcs12 -export -in "%TLCP_DIR%\client_sign.crt" -inkey "%TLCP_DIR%\client_sign.key" -out "%TLCP_DIR%\tlcp-client-sign.p12" -password pass:%PASS% -name "client.sign" +"%OPENSSL%" pkcs12 -export -in "%TLCP_DIR%\client_enc.crt" -inkey "%TLCP_DIR%\client_enc.key" -out "%TLCP_DIR%\tlcp-client-enc.p12" -password pass:%PASS% -name "client.enc" + +del /q "%TLS_DIR%\*.csr" "%TLS_DIR%\*.srl" "%TLCP_DIR%\*.csr" "%TLCP_DIR%\*.srl" 2>nul +echo [fixtures] done. Password for all PKCS12 files: %PASS% diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt new file mode 100644 index 000000000000..c07b6c18a47c --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhjCCASugAwIBAgIUG2xUB4pMnW4AbOJ+S907pzNavxUwCgYIKoEcz1UBg3Uw +GDEWMBQGA1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2 +MjkwOTQzNTBaMBgxFjAUBgNVBAMMDUlvVERCIFRMQ1AgQ0EwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAQtFLNDgh39KkMKMNH2LZBu4dFaSAK1+tTyK7Q+f3sh+hDg +HmT3jsGDqkkshX1dUu2H1rxGhYp2jbp7XH/cOsg1o1MwUTAdBgNVHQ4EFgQUxuRe +jrAZtH5PGdo/JtL1nzzfU0UwHwYDVR0jBBgwFoAUxuRejrAZtH5PGdo/JtL1nzzf +U0UwDwYDVR0TAQH/BAUwAwEB/zAKBggqgRzPVQGDdQNJADBGAiEAh83vSwujQPqQ +LXCoSiPnHndpIMTar2MNH3HvKBRDxJYCIQDeyUdqO12TnmcyqgAevUVzdzbb/mPQ +Opj6J+PCsxyA7Q== +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt new file mode 100644 index 000000000000..0911322ab0d2 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBKDCBzgIUPu9HMMReU/2p77newOOfrdaIP3YwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz +NTBaMBUxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAASsbddNsVBXsoEv5rs0rPmppN5ahGf7Thsb8AWbj3GwiW2X3Gy7PJGck/kW +ilJP9hGtYpS2Eo/TPXNLqtPcz8DVMAoGCCqBHM9VAYN1A0kAMEYCIQCqYsG+mF14 +adeZf086xrgVHfigfyCL0HdMlx0lxCSLfwIhAOM9yM8ogZ4gGnMlmDdnQLijNbGX +EIjTxunW0kFjg9Ew +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key new file mode 100644 index 000000000000..e544ad4be49d --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg+Jw4VYt3t1WumoqR +dC9UO5tZoQ4SzjeyP9AW4fs5uMuhRANCAASsbddNsVBXsoEv5rs0rPmppN5ahGf7 +Thsb8AWbj3GwiW2X3Gy7PJGck/kWilJP9hGtYpS2Eo/TPXNLqtPcz8DV +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt new file mode 100644 index 000000000000..76c32ae7b1d2 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBKDCBzwIUIgMPVhPEAo02eT9LxrqQySqxxckwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz +NTBaMBYxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAEEmlwmVJKK64nKIVBDMFxcmcWC+EecnWvLOAqanP6d2M9j6sYUtEHX+4o +4gzkw17bzEyeg49INkZpUusKRpTxYDAKBggqgRzPVQGDdQNIADBFAiB6TfpgrH24 +r/HdAgsoiG4ZqJdUcqcPovzmhxXnzlnzgwIhAL86MbJg8MAXe7JQPdca2Hc5VweK +PpkHcpr7inmVwXzZ +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key new file mode 100644 index 000000000000..85c3bd67761b --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgNNBfLhw/OzJefgeG +vt0l1L9i1nI6nTS5QBg6+dy1ulihRANCAAQSaXCZUkorricohUEMwXFyZxYL4R5y +da8s4Cpqc/p3Yz2PqxhS0Qdf7ijiDOTDXtvMTJ6Dj0g2RmlS6wpGlPFg +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt new file mode 100644 index 000000000000..98377e1a51aa --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBJzCBzgIUdQacDdq1cFAUNA6808zmxcdkLw4wCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz +NTBaMBUxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAATBSLlntFGVZdBuYraBVmx/+lcaYeS34+LZQhzZmbRURERpZzEUGYSllWtA +as6u64Ch8Ta8KOBPB/QZiGRdce1BMAoGCCqBHM9VAYN1A0gAMEUCIE8sxrEWDTxR +MLkt/m4CaQgEI8dZN+WnSiYfCknNwT7GAiEAx9IyYLLtzZngErfgV8qDhZ/Ir38D +yJaLPlJHrjH1eVc= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key new file mode 100644 index 000000000000..8f444121586e --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5JLkxeJcclsVHu1G +o3LmU2dZCAEfZo6Xed0nL3NCLb6hRANCAATBSLlntFGVZdBuYraBVmx/+lcaYeS3 +4+LZQhzZmbRURERpZzEUGYSllWtAas6u64Ch8Ta8KOBPB/QZiGRdce1B +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt new file mode 100644 index 000000000000..8a0acf250709 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBKTCBzwIUbumqE+eQaU19UvFTeUL/l/RCxkswCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz +NTBaMBYxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAEDY3huaTyqILrM28nzpcTXVcPWyn0yqqEwdxXWo0uWcmVi5z4o9yDOViJ +0kGNfhbjNkq5O5v2oHLpQxOoopuUbzAKBggqgRzPVQGDdQNJADBGAiEA52Fb/Lxl +k0i2adwbzx9r8UNbtAmWO6IDhGFZZ/o2ljYCIQCC8VplDWGvC7FDPflO2VXZ0wIz +dBztr8q4k+Iu5W/lXQ== +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key new file mode 100644 index 000000000000..794fd1e00265 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgwvH6+d1hrPRPG06J +GPR6La8WweLCNgUfsvpcDa2hAsehRANCAAQNjeG5pPKoguszbyfOlxNdVw9bKfTK +qoTB3FdajS5ZyZWLnPij3IM5WInSQY1+FuM2Srk7m/agculDE6iim5Rv +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 new file mode 100644 index 0000000000000000000000000000000000000000..39a3eb977ef4cdc45cb83013f68ab5e5f9ed5e85 GIT binary patch literal 1029 zcmXqLVqs)rWHxAG-p;Tc0E7F#Kgg1z{AD?v5kq7mBB!kjWeOm zgE5tvg;9$|V3P9Y<&#!EkkactU{EQ|%-+z%xFK~#ljotAv;S~=CR_dLe0#u_E569Z zb&hAO`-kiETl^=77xr{S@0VXSMeh1vr=TOYR{e9gnXcA)eOGVO9Rb%y)uw~uoHBA- z=W6KrO%LR@Z^=#w@2)md+Oj-z!hxx6f(K_e{olS#PM&Gbzx7I4QS&xWIxmDRLUpT09_O=tL zYd8K~#^`-LPmAd>=W%{Ri*p}8EHyU^`TLPwR`nB${hF(3JB+3L{K}W|etMl>!ExRu z(qWly{i|Bj?`5|Ruc+UUJp1R$n_N0+k9OTzQG+MC3%s~&!(XyAPB^#T#@@P%J#$Vjf7Xxslvy9Q zecF0Fc+;CiUvJI2{l*#kR!Od!UljlOzc`>T$hPLr-s7wP-*|r6Tj_6^jQ-TAelL4x zt@wJkiG6mv4)4Q5J8p@+OWQJUwMF=g%($CQ!rO&cYT0T`{%Nc!ywiwRaDV6n7RRYy z+!wFWcyWBq`e*Km{_H1JPek(W@z+XpcHPbR;i93JfigUFI7JM_>>NN)1BLLSo4HsG~RiLFTw%7S;6C9rYPk#OK#+unhd!HW3ou$e8>-sCXH+7{oi{p8?tLto9WK%xxC&; zsBdA{!p5dcNy+MNs~&A+;4qxT5+tkh;SG0mk$-aZlhk}2^P0uB%6~NPKl-mAUoZD3 zSBJHz+Tn*>mDcI2u1Epvk=#teA;gAx>DBuf)xC{Q>Mh<(|(p=phWi;-zT<9ma~H-wVU z`^<$eJ+$t(5==g;TkV^>|E8?4@N!s{vBfC#6zWOx3 znDPCYhYwQraxMvQOI><#!pGb1x$7rQip)0jF;Iqw4yUN07>mfQ%)^zB6!y3~i5cje z;an`Y_g;Ijfht0gh@pg>7(+5c4nrnGDnlMa34L`-?yW|%C+w$6#lWv=y`hQgK*bn{t3%c9&u1{v+@W>KaE3?X_7!;rYUDA?I literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 new file mode 100644 index 0000000000000000000000000000000000000000..95db3ecab34702a905663ec954f31f828e636045 GIT binary patch literal 683 zcmXqLVp`6`$ZXKW6wAh`)#lOmotKfFaX}N4A4?OH7f{#@h@B8pY(ObvppZTjBLk4q zK*%r}gu^+kyarwdng}kBfeZ_W@0uqaLTe4%n3y;i40zZ$Aht1avN9ORvT-J~c`&9j zvoLD02x$9!VCOVEyybZcYfR7UHM$K=j0arVk4x9HF9^ym_!3|pqmJ4Lc3p^@MdWIJM+7H*32s{wkl8hGZTLFuKabOEI0YO z!StlZGUB4krTWB9%748Ppwa(^!FiriW&IYtR%U_F>Ce70MrB5au7;_X-V&b2r9G#;t$WW4BEduY<~y4am*w++u|o}ak8FJwYw!E5o9 zr^zn2-0m`Ph4(o8iZ*~-2qyO8W#cCs6ju@f;EB0`p8#E_IVTh{EM zjFNpA%PU**`k(hb_3{7Vo_n6>`EWn|o^ufx$N-Rv8i9dezz}$>R_s0#6)jaE2I5bH zfq0+U9taF|$KMECh@q}Kvj{*cz}YJQn@}Ol{|FEhLI)x7S27}aY3PhjFY7u!3T!e#cEMGQPWVtX`opNhjuRJWPDFH!#qWX?IZ0w+AD=@t59QKM2ZQAlm{@AeY%8@#_o`8^(Ro%kNBWw9BF2$NP%@DqkehR2hu&2`^ zW__au*}&Q+C#*XHuv5Jko2>TF8w{sc@x1_BrfW@f4;?ruu7)7mx(B{;M`PnV%jxeT!YH4NE2nnxx4j4!o@FZD9t%;s?Ki~$o9K)?X8K#T?M7fu;rg`2z%aQ;nQSO zU+`p`7W0BPN>bT2z%nc~YWM=$*6=`apwuYl*iLG0!+O%JJ|(m#7gw*4EvjlYOnHeK zfV-B*fD6aC#lfWDDH-3Pw?14E=|K$Ibyb{l&&9ntav%N-A$?Fa)~i%YSm8?>Inur> zZ14@Xq{xRoHDysgJiuM+6g>#ye|)}1V>{v!SU|QsXC(QV>nFtyrPTb@F$wU~{e|J9 zXS=m1{%SL!_x!E+!^X*~-yghzDv^|tEsYz~1{(DGjo}uD>6RXtr}uOgwQ;m9BgD!Y zb8F*{gun`keqt9y4;(33zWelaN7qA1gG=1F%@n;q?l=D!V#m=S1f4*e0*r#ok|oG| z*N&DyzJ`YFe9d|jZbA=ybFXlJ_lASPof)=JrXSO!DX)#H+wr)*08)_R-8_BG1@gR* zJ#F;eQcq+{yvfjw>Lwt;qyNf}>wb70S?1W!rNIKNzU)aVvSov4Vko%?FSYPX><;1! z_xmk2xZ-~t_SvXp4$4=5Ub#h~v=1N0Kd(tyTkPc7w2~FxF@!ud7(3lz8u73|U_eEG zc>D)&&=VRADD})vJeycB!~fZ5q^AO&g~&5g`rm-YL7NS}Z`LgRE6_#lk*=zC8kix2 zq~oSf*k&3G%_qROxL}m(uQZa<>L9KHSo>U!e>f^l>h}*+km|4<(=)g6<1~)=okt~! zqstBYt;4Q-Z0rJp_9ErsE6LTPK4MCHBz4|j$X2zk=UG~bzHSUtkFkgp_cEoteZbiYg?mNE`Hj7XR#!fmj3-XXUwtJ` z)VXdk#4R+NnLrS&gBt~CiaSQA6`<;vEo1v^a+H^3h;3iUUmH)p*3@%m=ByEAb#rW4 z@F@9?U5#LCW*@P9G!}5%nR+ij`#vj!mngaD>!AsHieK?6%)n3;-xMz*-7VjYOT-7S zuH^Rm4Q9ZU-!c;{vseMU0?xbOS@**lk%jCzD51tDEMINtymk)DIjS(56OF;0(i%!@ zxN>H=#~#+eIjVpcq=BigI)}}9AwA>7jZ_Sf1p4G%0K3>EJxJzwIn}~{pZW{a*0N#0 zmaFg9o(|z&Q(wZ#5so_Wr$Q7xn=usVfj~kZ~}J|KOU_Cb@Ld`R%%t zLVJ%TuEhvsajrs4fVSaau=T6V=a`%v zA$VF~|6;vS=(4pVE#(MZt_=HCmiLT6sMse??CWyf4itqRiPVHjC{wuUi^m|8js_ntJi@`I&hJ}g-<9SB}%OzxLc*Dl z%uJMIoLk<*9AXU1J_bp6(s`FEdn;$>6!}v6LNnSCbRH*>nW!${*eLw$l^@KJ=a=ND;6k0_L#++#q;c7Aj8LBm z5@IJ%pz&tC-;WBN5tk{I)V!vV2lDM$V;SBa!S0WCqWz{kT}lk)_d7R^J>_*uGIcW) z4&J>ME@PsbWlMloZTk;=R?f!_?SeZrq#}91y!j$CWOXnguuJK^$u;^IWNuD9TSB;f+7aB0J3y!+kO-zfzYvfL z1fUUIoZ$8UQ5?I7oOf#?O8J6CqRnE&B)*S>^EAhjZkAfp(A~~BS~huDob*o*{})tA BiLw9y literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 b/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 new file mode 100644 index 0000000000000000000000000000000000000000..67bf7ae35ea3a36c39d3b3a82a8927525645bd61 GIT binary patch literal 2496 zcmai$X*d*$7shAIV1^lDNY^?y3gcoFvTxa?lr%EhM9S7!MwUT_6xk+2qhh98hV0kP z8rgToKK3nSi;!jP*M0u~r#{{f=bYz#-w)^0?>q-hgqE`bL1-e>ivx;^Fpb!P137^- zBJ?hp2)%V=W6?xV!LNveMg+Y(vZ!o8z|o-mN`Pq0Pr(jHo1bB|*9y0KnkKNs#|O&?uMp9yjCWy=K=6APX-EYQpx_oEalCvHT!o~$rzE|xrd zET-|}*GP{G>Mt3J6XcNHW|$(0L4Pg~bT!AKDJa2!@20k!EzE6QJgw|@b{@`$hu-gy zg#9C40BLD=R$X*v@TD5z6l*$5?u3b)!lqI{5b;~Zj2XNxw`( zf$0ju8qK{AQ-B?gZ)^B!uf8>6oX=So5lh_R=RX`q;r2J^nUB~FW<1_LIp(K0pW^H9 zG+0+B#i;68j>p^(fSy@P;L zyL*56%u*IKLUbUZKk%OXxfpsaQoZ>WEnvL7LI(7%)${!LhsjQSHvUJ1Zb>QQMPnvgZj`pn z6I5o<1HD6)6+PXx%y^vMM+xok>+sUZS?t~^O;vsvvR$P1p?fqek5RM$O3|HS;CqdG zRhNfU#%vbiOA^8#=Vw6;{J){zFu%w?5s*E*h1aRYv|bce(;?j>8Wm9TrLP7LZT?*V;ioc(iG$4GH@LB-DIF7Y||bB z;-CsY;u7p97=vn5&$V{~3<3|k^X%aYi`{FM;#Y1VER;nXrLne(vwx(M{o!dca$jx? zr5MCib4|c3G9->QT?~PyEk^Q4s{R#be<(S>PAA_3-i;|YoIgL9u+Qp9*%j1 z6G&f$_m?DG;}VN3G8QD<$~&8u1k0;jRSv(lTq{dU6&KglFXc&F!h5~Kco~!ubS_u% zF{SjO(mmdVkgvkcPb+I!AMbr$ugKwbwp-QhuhSPCXPKK@d&YORylq3}_3g=!?_ST^ zRSmdQ!#kh181UJ5RJJPF{!cNl42L!ZkB=f;D@oByho$Woki zbhVM$IAy|UbGs?Q)?LKkP)27@M$M^$Z@?i5>`cLSE56*ADQl?V;Z}xpt0rCDt>?vD z2z3Q;cR{igF|Q?V(cTNNs`AY`X*;eCF0RS6u9+t!jX=wSw?r1`o#7YWPR=XGuzHq@ z$MhwKR!$p>72Jf;JV3RXPNB?!3OJfA4cgAmJq!k!jk@!EXe_75SsNJH@+Zh;H*NV>iQF1f+f}J7&GXj22XEiDn%rN~ z)L5Hegg(`R?jbdFNXsVAp0qqj6oqhFy*ACmOv+`xjQ(tACc1yYAZQ3tRxKOL=^XRM z9Sf1rG!V3>NjmW$pP`26YORN<$5EF}2khYU!8^G0&?QZ3%z}=U9q)U?fh_4~q7L}Z z(rP3(01g=T!4@qN=;35^i{tMm)KR0+BZrFuVFo$q=R*Trk2{|NbmbK znqGDBqO%rXyp!Nq%Wo(KDv0bU6{1d|7MmWB-zN_c=)u`}V^=vDv51-n@ai8&9PYbsE-~xZ1S8<9vRS#HX z45Q?n#Gd08yz<(zp>FlDViL+fKxU+QUtdZ`wJ*2$o%F(eo94`HEiAO)N^h9VEW( zwGVr4Z(O0w@wT^K)J#28V(7OxtT&;qW@_@2k^x%e=YfR^DDjBH0Nwy6fEU2`-;S32 z6$&aH2Mavic`W2G%XYZH4TkEEjY(Mo9e&)4bW#K-`o zb|7RJ4Z`6ZR$c=y15E^%$3TXKgEg({t!3~52__~E1_K^84v1|`oU9B6vTU3QZ61uN z%q)yrECME0KkdXnC6+CJ{6CbBb3=YZ6LY}QY$18^h#xGKEAG#hWX{{SMf!Mq+NrvW z0gPHkM&->9JhZ`U?t||s%SD}cpWru(c0H8Aal7euzK{BywkaOg4Px`^B=6rpyKsJk z;stecPh~I7<;=o&4@WHM4Kod@0p`z-$0Sp9KoghJDM7wMeqnw51f${+du@K|#28p>v$T^(oht?_|z``#MU z2EOtr=~de*%$;I5W;b6l`uuFs#pu4peQv2&a%yg@Fj%uF(D!GgtMa_`|5qD2eI1Vl zvUn~$@%j6z>#KwcW`*l;{5QGsO4vd&xTQ65R?Um%WjsE)=})VB7e}4AeLAREVR}yC zM1x|k^fJwllbjdN5qNu<{qSd%&B=Z1PCh=k@!j2zeLjiTze-B^JXX{SpDj4EN8|g; zSC3S97n-$j3Y%~UtzN#jQl^1HI{WQapOpF91){z^AI^AuJ5}*{uAKXy5ALhJA5moW zDZVmi=l|xgBI}phKN;^U2EW{*qO(Rv<4EY&N6VN(i*Jklxa%tyf8kI2Ly7WNKfLc| zpF5s-M%-nmtIO4d&syu3bzPb)pqe#{bKCysmpB6!1i@OIO5<~z0Tm)!rL zAk?$$_Amd(TB+_&i}hB0kykvcb~Q$LY8khA>@)8Go1ebZ*t2fjSFiZWe~ZsFaci(x zU1{famxv2jy3`)Br2Jp0$NK(-@XQZq&gJN;l(MmeX5X^fTYq#~aTc@Wx1&Z3Ne{PM z^Gbz2vK8KUZAqH5(D}IF1ltXTa}Mf#^IZ`2QSxSb(7aR9k2IW4#J;_wwCcP@;X&O5 z?xs{@mN=*1yPhm6-n@8;-kT&2iPD>*D>Yn>tXb>v(q!q^o9`=+?Jhc|X|eKkzg|Z0 zH*t|iy))doJN3Rb^XTVZTT=GfKDq@3NI+ zt@+QWCSxF2F5JESf!xiqHpz*rF6wSQp&0Y{(CLND3C|524Gax<;n|Lfk(Gf(;oJEz vpQOJBCIwgJ)b^x#UTFEUaCV8QLYR4KmAlH+mvJl{e)qqX$G%DG2jyV^usYy# literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/main_rpc_ssl.cpp b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp new file mode 100644 index 000000000000..ec0bec9adb32 --- /dev/null +++ b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define CATCH_CONFIG_MAIN +#include diff --git a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp new file mode 100644 index 000000000000..063fe7ec79fe --- /dev/null +++ b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp @@ -0,0 +1,159 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +std::string joinPath(const std::string& base, const std::string& name) { + if (base.empty()) { + return name; + } + const char sep = (base.find('\\') != std::string::npos) ? '\\' : '/'; + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +EVP_PKEY* readPrivateKeyPem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return key; +} + +X509* readCertificatePem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return cert; +} + +void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { + unsigned char keyid[EVP_MAX_MD_SIZE]; + unsigned int keyidLen = 0; + if (X509_pubkey_digest(cert, EVP_sha1(), keyid, &keyidLen) == 1) { + PKCS12_add_localkeyid(bag, keyid, static_cast(keyidLen)); + } +} + +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, + const char* friendlyName, const std::string& password) { + PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); + PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); + addLocalKeyId(certbag, cert); + sk_PKCS12_SAFEBAG_push(bags, certbag); + + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + if (p8 == nullptr) { + return; + } + PKCS12_SAFEBAG* keybag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (keybag == nullptr) { + return; + } + PKCS12_add_friendlyname_utf8(keybag, friendlyName, -1); + addLocalKeyId(keybag, cert); + sk_PKCS12_SAFEBAG_push(bags, keybag); +} + +} // namespace + +int main(int argc, char** argv) { + if (argc < 2) { + std::cerr << "usage: gen_tlcp_dual_p12 \n"; + return 1; + } + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, nullptr); + const std::string dir = argv[1]; + const std::string password = "thrift"; + const std::string outPath = joinPath(dir, "tlcp-client-dual.p12"); + + X509* signCert = readCertificatePem(joinPath(dir, "client_sign.crt")); + EVP_PKEY* signKey = readPrivateKeyPem(joinPath(dir, "client_sign.key")); + X509* encCert = readCertificatePem(joinPath(dir, "client_enc.crt")); + EVP_PKEY* encKey = readPrivateKeyPem(joinPath(dir, "client_enc.key")); + if (signCert == nullptr || signKey == nullptr || encCert == nullptr || encKey == nullptr) { + std::cerr << "failed to read TLCP PEM fixtures\n"; + return 2; + } + + STACK_OF(PKCS12_SAFEBAG)* bags = sk_PKCS12_SAFEBAG_new_null(); + addCertAndKeyBags(bags, signCert, signKey, "client.sign", password); + addCertAndKeyBags(bags, encCert, encKey, "client.enc", password); + PKCS7* p7 = PKCS12_pack_p7encdata(NID_pbes2, password.c_str(), static_cast(password.size()), + nullptr, 0, 2048, bags); + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + if (p7 == nullptr) { + std::cerr << "failed to pack PKCS12 bags\n"; + return 3; + } + + PKCS12* p12 = PKCS12_init(NID_pkcs7_data); + STACK_OF(PKCS7)* safes = sk_PKCS7_new_null(); + sk_PKCS7_push(safes, p7); + if (PKCS12_pack_authsafes(p12, safes) != 1) { + sk_PKCS7_free(safes); + PKCS12_free(p12); + std::cerr << "failed to pack PKCS12 authsafes\n"; + return 4; + } + sk_PKCS7_free(safes); + + BIO* bio = BIO_new_file(outPath.c_str(), "wb"); + if (bio == nullptr || i2d_PKCS12_bio(bio, p12) != 1) { + std::cerr << "failed to write " << outPath << "\n"; + BIO_free(bio); + PKCS12_free(p12); + return 5; + } + BIO_free(bio); + PKCS12_free(p12); + X509_free(signCert); + EVP_PKEY_free(signKey); + X509_free(encCert); + EVP_PKEY_free(encKey); + std::cout << "wrote " << outPath << "\n"; + return 0; +} From b9327738a841e40bb08a90dbff3a84b807c5560e Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 20:53:41 +0800 Subject: [PATCH 03/24] Fix Linux SSL test server launch and bundle runtime libs for ctest. Use execv with the correct openssl argv on Unix, disable LeakSanitizer for rpcSslUtilsTest, and copy libiotdb_session plus OpenSSL shared libraries next to IT binaries on Linux. --- iotdb-client/client-cpp/test/CMakeLists.txt | 24 ++++++++++++++++++- .../client-cpp/test/cpp/SslTestFixtures.cpp | 11 +++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index 15c5991ff8e3..bdf1230b48ff 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -96,10 +96,16 @@ if(WITH_SSL) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) - foreach(_t IN LISTS _test_targets) + set(_iotdb_asan_targets session_tests session_relational_tests session_c_tests + session_c_relational_tests) + foreach(_t IN LISTS _iotdb_asan_targets) target_compile_options(${_t} PRIVATE -fsanitize=address -fno-omit-frame-pointer) target_link_options(${_t} PRIVATE -fsanitize=address) endforeach() + # OpenSSL/Tongsuo may report benign leaks; avoid failing rpcSslUtilsTest on LeakSanitizer. + target_compile_options(rpc_ssl_utils_tests PRIVATE -fsanitize=address -fno-sanitize=leak + -fno-omit-frame-pointer) + target_link_options(rpc_ssl_utils_tests PRIVATE -fsanitize=address) endif() # Linux: keep iotdb_session in the executable's needed-list even when there @@ -138,6 +144,22 @@ else() add_test(NAME sessionCIT COMMAND session_c_tests) add_test(NAME sessionCRelationalIT COMMAND session_c_relational_tests) add_test(NAME rpcSslUtilsTest COMMAND rpc_ssl_utils_tests) + foreach(_t IN LISTS _test_targets) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $ + COMMENT "Copy IoTDB runtime library next to ${_t}") + if(WITH_SSL) + foreach(_ssl_lib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY) + if(_ssl_lib AND EXISTS "${_ssl_lib}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_lib}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endif() + endforeach() + endif() + endforeach() endif() # Run sequentially: parallel ctest overloads the single local IoTDB instance. diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 0cb2fbe45110..80c6a61ccc9e 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -541,17 +541,18 @@ bool OpenSslServerProcess::start(const std::vector& args) { processId_ = pi.dwProcessId; CloseHandle(pi.hThread); #else - std::ostringstream command; - command << quoteArg(opensslExecutable()); - for (const std::string& arg : argStorage) { - command << ' ' << quoteArg(arg); + std::vector execArgv; + execArgv.reserve(argStorage.size() + 1); + for (std::string& arg : argStorage) { + execArgv.push_back(const_cast(arg.c_str())); } + execArgv.push_back(nullptr); const pid_t pid = fork(); if (pid < 0) { return false; } if (pid == 0) { - execl("/bin/sh", "sh", "-c", command.str().c_str(), static_cast(nullptr)); + execv(exe.c_str(), execArgv.data()); _exit(127); } childPid_ = pid; From 4c090242ac4ea8f79cbbdaac4be36e07376eb6a9 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 21:45:16 +0800 Subject: [PATCH 04/24] Set LD_LIBRARY_PATH when spawning Tongsuo openssl s_server in SSL tests. Linux rpcSslUtilsTest e2e cases need the bundled Tongsuo lib64 directory on the loader path for the child openssl process. --- iotdb-client/client-cpp/test/CMakeLists.txt | 5 ++++- .../client-cpp/test/cpp/SslTestFixtures.cpp | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index bdf1230b48ff..ec50e9d4a074 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -78,11 +78,14 @@ if(WITH_SSL) endif() file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" _iotdb_test_fixtures_dir) file(TO_CMAKE_PATH "${_iotdb_openssl_executable}" _iotdb_openssl_executable_cmake) + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir) string(REPLACE "\\" "/" _iotdb_test_fixtures_dir_fwd "${_iotdb_test_fixtures_dir}") string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir}") target_compile_definitions(rpc_ssl_utils_tests PRIVATE IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir_fwd}" - IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") add_custom_command(TARGET rpc_ssl_utils_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 80c6a61ccc9e..6feb8a0fc033 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #endif #if WITH_SSL @@ -284,6 +285,22 @@ std::string opensslExecutable() { #endif } +void prependOpenSslRuntimeToLdLibraryPath() { +#ifdef IOTDB_OPENSSL_ROOT_DIR + const std::string root = IOTDB_OPENSSL_ROOT_DIR; + std::string libPath = joinPath(root, "lib64"); + const std::string lib = joinPath(root, "lib"); + if (pathExists(lib)) { + libPath = libPath + ":" + lib; + } + const char* existing = std::getenv("LD_LIBRARY_PATH"); + if (existing != nullptr && existing[0] != '\0') { + libPath = libPath + ":" + existing; + } + setenv("LD_LIBRARY_PATH", libPath.c_str(), 1); +#endif +} + std::string quoteArg(const std::string& arg) { #if defined(_WIN32) return "\"" + arg + "\""; @@ -552,6 +569,7 @@ bool OpenSslServerProcess::start(const std::vector& args) { return false; } if (pid == 0) { + prependOpenSslRuntimeToLdLibraryPath(); execv(exe.c_str(), execArgv.data()); _exit(127); } From 07e78145cf0563032c9aefe6aa64c0786362eb3e Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 21:52:17 +0800 Subject: [PATCH 05/24] Disable LeakSanitizer for rpcSslUtilsTest under ctest on Linux. OpenSSL/Tongsuo reports benign allocations at process exit; ASAN_OPTIONS=detect_leaks=0 keeps address checks without failing the SSL unit tests. --- iotdb-client/client-cpp/test/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index ec50e9d4a074..6f538b940172 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -169,3 +169,11 @@ endif() set_tests_properties( sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT rpcSslUtilsTest PROPERTIES RUN_SERIAL TRUE) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) + set_tests_properties(rpcSslUtilsTest PROPERTIES + ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") +endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) + set_tests_properties(rpcSslUtilsTest PROPERTIES + ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") +endif() From e2c507e852ddcb0ce617213cc46b53958c7e5ea5 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 10:03:22 +0800 Subject: [PATCH 06/24] Fix PKCS12 memory leaks in C++ SSL client and tests. Free PKCS7 safes after PKCS12_pack_authsafes, add RAII for parsed PKCS12 identities, and remove LeakSanitizer workarounds for rpcSslUtilsTest. --- .../client-cpp/src/rpc/RpcSslUtils.cpp | 124 ++++++++---------- iotdb-client/client-cpp/test/CMakeLists.txt | 14 +- .../client-cpp/test/cpp/SslTestFixtures.cpp | 28 ++-- .../client-cpp/test/tools/GenTlcpDualP12.cpp | 4 +- 4 files changed, 81 insertions(+), 89 deletions(-) diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp index 3410cabc2c24..febdcc91f9ce 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -126,6 +126,36 @@ PKCS12* loadPkcs12(const std::string& path, const std::string& password) { return p12; } +struct Pkcs12ParsedIdentity { + EVP_PKEY* pkey = nullptr; + X509* cert = nullptr; + STACK_OF(X509)* ca = nullptr; + + ~Pkcs12ParsedIdentity() { + if (pkey != nullptr) { + EVP_PKEY_free(pkey); + } + if (cert != nullptr) { + X509_free(cert); + } + if (ca != nullptr) { + sk_X509_pop_free(ca, X509_free); + } + } + + Pkcs12ParsedIdentity() = default; + Pkcs12ParsedIdentity(const Pkcs12ParsedIdentity&) = delete; + Pkcs12ParsedIdentity& operator=(const Pkcs12ParsedIdentity&) = delete; +}; + +void parsePkcs12OrThrow(PKCS12* p12, const std::string& password, Pkcs12ParsedIdentity& parsed, + const std::string& label) { + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) != 1) { + throwSslError("Failed to parse PKCS12 " + label); + } +} + std::string getBagFriendlyName(PKCS12_SAFEBAG* bag) { char* name = PKCS12_get_friendlyname(bag); if (name == nullptr) { @@ -218,34 +248,22 @@ void addCertToStore(X509_STORE* store, X509* cert) { void loadTrustFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { PKCS12* p12 = loadPkcs12(path, password); - EVP_PKEY* pkey = nullptr; - X509* cert = nullptr; - STACK_OF(X509)* ca = nullptr; - if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { - PKCS12_free(p12); - throwSslError("Failed to parse PKCS12 trust store " + path); - } + Pkcs12ParsedIdentity parsed; + parsePkcs12OrThrow(p12, password, parsed, "trust store " + path); X509_STORE* store = SSL_CTX_get_cert_store(ctx); - if (cert != nullptr) { - validateCertificate(cert); - addCertToStore(store, cert); - X509_free(cert); + if (parsed.cert != nullptr) { + validateCertificate(parsed.cert); + addCertToStore(store, parsed.cert); } - if (ca != nullptr) { - for (int i = 0; i < sk_X509_num(ca); ++i) { - X509* caCert = sk_X509_value(ca, i); + if (parsed.ca != nullptr) { + for (int i = 0; i < sk_X509_num(parsed.ca); ++i) { + X509* caCert = sk_X509_value(parsed.ca, i); validateCertificate(caCert); addCertToStore(store, caCert); } - sk_X509_pop_free(ca, X509_free); - } - if (pkey != nullptr) { - EVP_PKEY_free(pkey); } - STACK_OF(PKCS12_SAFEBAG)* unusedBags = nullptr; - (void)unusedBags; forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { X509* bagCert = PKCS12_certbag2x509(bag); @@ -277,32 +295,19 @@ void loadTrustStore(SSL_CTX* ctx, const std::string& path, const std::string& pa void loadTlsIdentityFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { PKCS12* p12 = loadPkcs12(path, password); - EVP_PKEY* pkey = nullptr; - X509* cert = nullptr; - STACK_OF(X509)* ca = nullptr; - if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { - PKCS12_free(p12); - throwSslError("Failed to parse PKCS12 key store " + path); - } - if (SSL_CTX_use_certificate(ctx, cert) != 1) { + Pkcs12ParsedIdentity parsed; + parsePkcs12OrThrow(p12, password, parsed, "key store " + path); + PKCS12_free(p12); + + if (SSL_CTX_use_certificate(ctx, parsed.cert) != 1) { throwSslError("Failed to load client certificate from " + path); } - if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { + if (SSL_CTX_use_PrivateKey(ctx, parsed.pkey) != 1) { throwSslError("Failed to load client private key from " + path); } if (SSL_CTX_check_private_key(ctx) != 1) { throwSslError("Client certificate and private key do not match in " + path); } - if (ca != nullptr) { - sk_X509_pop_free(ca, X509_free); - } - if (cert != nullptr) { - X509_free(cert); - } - if (pkey != nullptr) { - EVP_PKEY_free(pkey); - } - PKCS12_free(p12); } void loadTlsIdentityFromPem(SSL_CTX* ctx, const std::string& path) { @@ -389,21 +394,14 @@ void loadTlcpKeyStoreFromPkcs12(SSL_CTX* ctx, const std::string& path, const std PKCS12* p12 = loadPkcs12(path, password); TlcpIdentity identity; - EVP_PKEY* parsedKey = nullptr; - X509* parsedCert = nullptr; - STACK_OF(X509)* ca = nullptr; - if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsedKey, &parsedCert, - &ca) == 1) { - assignTlcpMaterial(identity, "sign", parsedCert, parsedKey); - parsedCert = nullptr; - parsedKey = nullptr; - } - if (ca != nullptr) { - sk_X509_pop_free(ca, X509_free); + Pkcs12ParsedIdentity parsed; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) == 1) { + assignTlcpMaterial(identity, "sign", parsed.cert, parsed.pkey); + parsed.cert = nullptr; + parsed.pkey = nullptr; } - STACK_OF(PKCS12_SAFEBAG)* unusedBags = nullptr; - (void)unusedBags; forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { const std::string friendlyName = getBagFriendlyName(bag); const int bagType = PKCS12_SAFEBAG_get_nid(bag); @@ -534,25 +532,19 @@ SSL_CTX* createTlcpClientContext(const SslConfig& config) { void validatePkcs12Store(const std::string& path, const std::string& password) { PKCS12* p12 = loadPkcs12(path, password); - EVP_PKEY* pkey = nullptr; - X509* cert = nullptr; - STACK_OF(X509)* ca = nullptr; - if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &pkey, &cert, &ca) != 1) { + Pkcs12ParsedIdentity parsed; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) != 1) { PKCS12_free(p12); throw IoTDBException("Failed to parse PKCS12 store: " + path); } - if (cert != nullptr) { - validateCertificate(cert); - X509_free(cert); + if (parsed.cert != nullptr) { + validateCertificate(parsed.cert); } - if (ca != nullptr) { - for (int i = 0; i < sk_X509_num(ca); ++i) { - validateCertificate(sk_X509_value(ca, i)); + if (parsed.ca != nullptr) { + for (int i = 0; i < sk_X509_num(parsed.ca); ++i) { + validateCertificate(sk_X509_value(parsed.ca, i)); } - sk_X509_pop_free(ca, X509_free); - } - if (pkey != nullptr) { - EVP_PKEY_free(pkey); } forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index 6f538b940172..c147bb7cab34 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -100,15 +100,11 @@ endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) set(_iotdb_asan_targets session_tests session_relational_tests session_c_tests - session_c_relational_tests) + session_c_relational_tests rpc_ssl_utils_tests) foreach(_t IN LISTS _iotdb_asan_targets) target_compile_options(${_t} PRIVATE -fsanitize=address -fno-omit-frame-pointer) target_link_options(${_t} PRIVATE -fsanitize=address) endforeach() - # OpenSSL/Tongsuo may report benign leaks; avoid failing rpcSslUtilsTest on LeakSanitizer. - target_compile_options(rpc_ssl_utils_tests PRIVATE -fsanitize=address -fno-sanitize=leak - -fno-omit-frame-pointer) - target_link_options(rpc_ssl_utils_tests PRIVATE -fsanitize=address) endif() # Linux: keep iotdb_session in the executable's needed-list even when there @@ -169,11 +165,3 @@ endif() set_tests_properties( sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT rpcSslUtilsTest PROPERTIES RUN_SERIAL TRUE) -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) - set_tests_properties(rpcSslUtilsTest PROPERTIES - ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") -endif() -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) - set_tests_properties(rpcSslUtilsTest PROPERTIES - ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") -endif() diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 6feb8a0fc033..8c27ec0952a8 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -233,11 +233,13 @@ void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, X509* cert = PKCS12_certbag2x509(bag); if (cert != nullptr) { PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_cert(cert); - if (friendlyName != nullptr) { - PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); - } - sk_PKCS12_SAFEBAG_push(target, newBag); X509_free(cert); + if (newBag != nullptr) { + if (friendlyName != nullptr) { + PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); + } + sk_PKCS12_SAFEBAG_push(target, newBag); + } } } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { EVP_PKEY* key = nullptr; @@ -327,7 +329,6 @@ std::string tlcpFixture(const std::string& name) { } std::string buildTlcpDualKeyStoreP12() { - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, nullptr); const std::string password = kStorePassword; const std::string outPath = joinPath(executableDir(), "tlcp-client-dual.p12"); @@ -356,18 +357,29 @@ std::string buildTlcpDualKeyStoreP12() { } return ""; } - for (int i = 0; i < sk_PKCS7_num(encSafes); ++i) { - sk_PKCS7_push(safes, sk_PKCS7_value(encSafes, i)); + while (sk_PKCS7_num(encSafes) > 0) { + PKCS7* p7 = sk_PKCS7_pop(encSafes); + if (p7 == nullptr || sk_PKCS7_push(safes, p7) == 0) { + PKCS7_free(p7); + sk_PKCS7_pop_free(safes, PKCS7_free); + sk_PKCS7_pop_free(encSafes, PKCS7_free); + return ""; + } } sk_PKCS7_free(encSafes); PKCS12* p12 = PKCS12_init(NID_pkcs7_data); + if (p12 == nullptr) { + sk_PKCS7_pop_free(safes, PKCS7_free); + return ""; + } if (PKCS12_pack_authsafes(p12, safes) != 1) { sk_PKCS7_pop_free(safes, PKCS7_free); PKCS12_free(p12); return ""; } - sk_PKCS7_free(safes); + // PKCS12_pack_authsafes only encodes safes into p12; it does not take ownership. + sk_PKCS7_pop_free(safes, PKCS7_free); const bool written = writePkcs12File(p12, outPath); PKCS12_free(p12); diff --git a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp index 063fe7ec79fe..48a9fc5790cf 100644 --- a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp +++ b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp @@ -134,12 +134,12 @@ int main(int argc, char** argv) { STACK_OF(PKCS7)* safes = sk_PKCS7_new_null(); sk_PKCS7_push(safes, p7); if (PKCS12_pack_authsafes(p12, safes) != 1) { - sk_PKCS7_free(safes); + sk_PKCS7_pop_free(safes, PKCS7_free); PKCS12_free(p12); std::cerr << "failed to pack PKCS12 authsafes\n"; return 4; } - sk_PKCS7_free(safes); + sk_PKCS7_pop_free(safes, PKCS7_free); BIO* bio = BIO_new_file(outPath.c_str(), "wb"); if (bio == nullptr || i2d_PKCS12_bio(bio, p12) != 1) { From 28dbecf31f642d74d92d5d4bb15d4012808a0eef Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 10:53:31 +0800 Subject: [PATCH 07/24] Guard Linux-only setenv helper for Windows SSL test build. --- iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 8c27ec0952a8..05659823ef16 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -287,6 +287,7 @@ std::string opensslExecutable() { #endif } +#if !defined(_WIN32) void prependOpenSslRuntimeToLdLibraryPath() { #ifdef IOTDB_OPENSSL_ROOT_DIR const std::string root = IOTDB_OPENSSL_ROOT_DIR; @@ -302,6 +303,7 @@ void prependOpenSslRuntimeToLdLibraryPath() { setenv("LD_LIBRARY_PATH", libPath.c_str(), 1); #endif } +#endif std::string quoteArg(const std::string& arg) { #if defined(_WIN32) From e9639d43a7bf3bdfcd1b942b861de56246602bec Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 14:46:41 +0800 Subject: [PATCH 08/24] Split C++ client IT into plain/TLS/NTLS phases and reorganize examples. Session IT and plain examples run against non-encrypted IoTDB; rpc SSL tests restart IoTDB with TLS. NTLS rpc tests and TLCP handshake examples use local openssl s_server. Add focused C++/C example smoke tests and IoTDB TLS E2E coverage. --- iotdb-client/client-cpp/CMakeLists.txt | 1 - .../AlignedTimeseriesSessionExample.cpp | 1 + .../client-cpp/examples/CMakeLists.txt | 95 +++++++-- .../examples/ExampleNtlsHandshake.cpp | 75 ++++++++ .../examples/ExampleNtlsHandshake.h | 34 ++++ .../client-cpp/examples/ExampleTlsConfig.cpp | 136 +++++++++++++ .../client-cpp/examples/ExampleTlsConfig.h | 53 ++++++ iotdb-client/client-cpp/examples/README.md | 16 +- .../client-cpp/examples/SessionExample.cpp | 1 + .../examples/TableModelSessionExample.cpp | 25 ++- .../client-cpp/examples/c_ntls_example.c | 31 +++ .../client-cpp/examples/cpp_ntls_example.cpp | 31 +++ .../client-cpp/examples/cpp_table_example.cpp | 57 ++++++ .../client-cpp/examples/cpp_tls_example.cpp | 62 ++++++ .../client-cpp/examples/cpp_tree_example.cpp | 65 +++++++ .../client-cpp/examples/tls_tree_example.c | 86 +++++++++ iotdb-client/client-cpp/pom.xml | 47 ++++- iotdb-client/client-cpp/test/CMakeLists.txt | 66 +++++-- .../client-cpp/test/cpp/ItSslConnection.cpp | 180 ++++++++++++++++++ .../client-cpp/test/cpp/ItSslConnection.h | 59 ++++++ .../client-cpp/test/cpp/RpcNtlsE2eTest.cpp | 113 +++++++++++ .../test/cpp/RpcSslIotdbE2eTest.cpp | 175 +++++++++++++++++ .../test/cpp/RpcSslTlcpMutualAuthTest.cpp | 60 ++++++ ...thTest.cpp => RpcSslTlsMutualAuthTest.cpp} | 69 ------- .../test/cpp/sessionCRelationalIT.cpp | 1 + .../client-cpp/test/cpp/sessionIT.cpp | 58 +++--- .../test/cpp/sessionRelationalIT.cpp | 8 +- .../test/fixtures/generate_fixtures.cmd | 4 +- .../client-cpp/test/fixtures/tlcp/ca.crt | 18 +- .../test/fixtures/tlcp/client_enc.crt | 14 +- .../test/fixtures/tlcp/client_enc.key | 6 +- .../test/fixtures/tlcp/client_sign.crt | 14 +- .../test/fixtures/tlcp/client_sign.key | 6 +- .../test/fixtures/tlcp/server_enc.crt | 14 +- .../test/fixtures/tlcp/server_enc.key | 6 +- .../test/fixtures/tlcp/server_sign.crt | 14 +- .../test/fixtures/tlcp/server_sign.key | 6 +- .../test/fixtures/tlcp/tlcp-client-enc.p12 | Bin 1029 -> 1029 bytes .../test/fixtures/tlcp/tlcp-client-sign.p12 | Bin 1031 -> 1031 bytes .../test/fixtures/tlcp/tlcp-trust.p12 | Bin 683 -> 683 bytes .../client-cpp/test/fixtures/tls/ca.crt | 34 ++-- .../client-cpp/test/fixtures/tls/client.crt | 30 +-- .../client-cpp/test/fixtures/tls/client.key | 52 ++--- .../client-cpp/test/fixtures/tls/server.crt | 32 ++-- .../client-cpp/test/fixtures/tls/server.key | 52 ++--- .../test/fixtures/tls/tls-client.p12 | Bin 2512 -> 2512 bytes .../test/fixtures/tls/tls-server.p12 | Bin 2496 -> 2608 bytes .../test/fixtures/tls/tls-trust.p12 | Bin 1083 -> 1083 bytes iotdb-client/client-cpp/test/main.cpp | 8 +- .../client-cpp/test/main_Relational.cpp | 4 +- .../client-cpp/test/main_rpc_ntls.cpp | 21 ++ .../test/scripts/configure_iotdb_ssl_it.py | 128 +++++++++++++ .../test/scripts/run_cpp_it_phases.py | 104 ++++++++++ 53 files changed, 1857 insertions(+), 315 deletions(-) create mode 100644 iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp create mode 100644 iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h create mode 100644 iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp create mode 100644 iotdb-client/client-cpp/examples/ExampleTlsConfig.h create mode 100644 iotdb-client/client-cpp/examples/c_ntls_example.c create mode 100644 iotdb-client/client-cpp/examples/cpp_ntls_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_table_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_tls_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_tree_example.cpp create mode 100644 iotdb-client/client-cpp/examples/tls_tree_example.c create mode 100644 iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/ItSslConnection.h create mode 100644 iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp rename iotdb-client/client-cpp/test/cpp/{RpcSslMutualAuthTest.cpp => RpcSslTlsMutualAuthTest.cpp} (63%) create mode 100644 iotdb-client/client-cpp/test/main_rpc_ntls.cpp create mode 100644 iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py create mode 100644 iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index 521fe9faf64c..e654c5bb037c 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -309,7 +309,6 @@ install(FILES "${CMAKE_BINARY_DIR}/package-metadata/VERSION" "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt" DESTINATION .) - if(BUILD_TESTING) enable_testing() add_subdirectory(test) diff --git a/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp b/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp index 80d1caadd353..5d2154193e2d 100644 --- a/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp +++ b/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp @@ -415,6 +415,7 @@ int main() { session->close(); delete session; + session = nullptr; cout << "finished\n" << endl; return 0; diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index 7fa5bae86661..fd0c4ecd41e3 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -141,17 +141,43 @@ ADD_EXECUTABLE(SessionExample SessionExample.cpp) ADD_EXECUTABLE(AlignedTimeseriesSessionExample AlignedTimeseriesSessionExample.cpp) ADD_EXECUTABLE(TableModelSessionExample TableModelSessionExample.cpp) ADD_EXECUTABLE(MultiSvrNodeClient MultiSvrNodeClient.cpp) +ADD_EXECUTABLE(cpp_tree_example cpp_tree_example.cpp) +ADD_EXECUTABLE(cpp_table_example cpp_table_example.cpp) +ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) +ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) ADD_EXECUTABLE(tree_example tree_example.c) ADD_EXECUTABLE(table_example table_example.c) +ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) +ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) set(_example_targets SessionExample AlignedTimeseriesSessionExample TableModelSessionExample MultiSvrNodeClient + cpp_tree_example + cpp_table_example + cpp_tls_example + cpp_ntls_example + tree_example + table_example + tls_tree_example + c_ntls_example) + +set(_it_plain_examples + cpp_tree_example + cpp_table_example tree_example table_example) +set(_it_ssl_examples + cpp_tls_example + tls_tree_example) + +set(_it_ntls_examples + cpp_ntls_example + c_ntls_example) + foreach(_t IN LISTS _example_targets) if(WITH_SSL AND _iotdb_ssl_link_libs) target_link_libraries(${_t} PRIVATE "${_iotdb_link_lib}" ${_iotdb_ssl_link_libs}) @@ -205,6 +231,48 @@ foreach(_t IN LISTS _example_targets) endif() endforeach() +if(_iotdb_examples_in_tree AND WITH_SSL AND IOTDB_EXAMPLES_REGISTER_TESTS) + file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../test/fixtures" _iotdb_example_fixtures_dir) + string(REPLACE "\\" "/" _iotdb_example_fixtures_dir_fwd "${_iotdb_example_fixtures_dir}") + foreach(_t IN LISTS _it_ssl_examples _it_ntls_examples) + target_compile_definitions(${_t} PRIVATE + IOTDB_TEST_FIXTURES_DIR="${_iotdb_example_fixtures_dir_fwd}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/../test/fixtures" + "$/fixtures" + COMMENT "Copy SSL test fixtures next to ${_t}") + endforeach() + foreach(_t IN LISTS _it_ntls_examples) + target_sources(${_t} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../test/cpp/SslTestFixtures.cpp") + target_include_directories(${_t} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../test/cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/rpc" + "${THRIFT_GEN_CPP_DIR}" + "${THRIFT_INCLUDE_DIR}") + if(BOOST_INCLUDE_DIR) + target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") + endif() + if(BOOST_INCLUDE_DIR) + target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") + endif() + if(WIN32) + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl.exe" _iotdb_openssl_executable_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + target_compile_definitions(${_t} PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + else() + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl" _iotdb_openssl_executable_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + target_compile_definitions(${_t} PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + target_link_libraries(${_t} PRIVATE iotdb_thrift_static) + endif() + endforeach() +endif() + # Optional: stage a self-contained folder for copying to another machine (see package README). set(_example_dist_dir "${CMAKE_BINARY_DIR}/dist") add_custom_target(example-dist DEPENDS ${_example_targets} @@ -229,20 +297,19 @@ foreach(_ssl_lib IN LISTS _iotdb_bundled_ssl_runtime) endforeach() if(IOTDB_EXAMPLES_REGISTER_TESTS) - set(_runnable_example_targets - SessionExample - AlignedTimeseriesSessionExample - TableModelSessionExample - tree_example - table_example) - foreach(_t IN LISTS _runnable_example_targets) + foreach(_t IN LISTS _it_plain_examples) + add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "plain" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) + endforeach() + foreach(_t IN LISTS _it_ssl_examples) + add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "ssl" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) + endforeach() + foreach(_t IN LISTS _it_ntls_examples) add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "ntls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) endforeach() - set_tests_properties( - example_SessionExample - example_AlignedTimeseriesSessionExample - example_TableModelSessionExample - example_tree_example - example_table_example - PROPERTIES RUN_SERIAL TRUE) endif() diff --git a/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp new file mode 100644 index 000000000000..89cd9e17ab36 --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ExampleNtlsHandshake.h" + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +extern "C" int example_run_tlcp_handshake(void) { +#if !WITH_SSL + return 1; +#else + const std::string caFile = ssltest::tlcpFixture("ca.crt"); + const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); + const std::string signKey = ssltest::tlcpFixture("server_sign.key"); + const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); + const std::string encKey = ssltest::tlcpFixture("server_enc.key"); + if (!fixtureExists(caFile) || !fixtureExists(signCert) || !fixtureExists(signKey) || + !fixtureExists(encCert) || !fixtureExists(encKey)) { + return 1; + } + + ssltest::OpenSslServerProcess server; + if (!server.start({ + "-enable_ntls", + "-ntls", + "-CAfile", caFile, + "-sign_cert", signCert, + "-sign_key", signKey, + "-enc_cert", encCert, + "-enc_key", encKey, + "-www", + })) { + return 1; + } + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + + const bool ok = ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port()); + server.stop(); + return ok ? 0 : 1; +#endif +} diff --git a/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h new file mode 100644 index 000000000000..69ab77ab9267 --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_EXAMPLE_NTLS_HANDSHAKE_H +#define IOTDB_EXAMPLE_NTLS_HANDSHAKE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Run TLCP one-way handshake against a local openssl NTLS s_server. Returns 0 on success. */ +int example_run_tlcp_handshake(void); + +#ifdef __cplusplus +} +#endif + +#endif // IOTDB_EXAMPLE_NTLS_HANDSHAKE_H diff --git a/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp b/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp new file mode 100644 index 000000000000..7204174a3923 --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp @@ -0,0 +1,136 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "Session.h" +#include "TableSession.h" +#include "ExampleTlsConfig.h" + +#include +#include + +namespace { + +constexpr const char* kStorePassword = "thrift"; + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +#if defined(_WIN32) +#include +#else +#include +#endif + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string trustStorePath() { + static const std::string path = [] { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + const std::string configuredPath = joinPath(joinPath(configured, "tls"), "tls-trust.p12"); + if (pathExists(configuredPath)) { + return configuredPath; + } +#endif + const std::string copied = joinPath(joinPath(executableDir(), "fixtures"), "tls/tls-trust.p12"); + return copied; + }(); + return path; +} + +} // namespace + +extern "C" const char* example_tls_trust_store_path(void) { + static std::string path = trustStorePath(); + return path.c_str(); +} + +extern "C" void example_tls_configure_tree_session(CSession* session) { + if (session == nullptr) { + return; + } + ts_session_set_use_ssl(session, true); + ts_session_set_ssl_protocol(session, "TLS"); + ts_session_set_trust_store(session, example_tls_trust_store_path(), kStorePassword); +} + +extern "C" void example_tls_configure_table_session(CTableSession* session) { + if (session == nullptr) { + return; + } + ts_table_session_set_use_ssl(session, true); + ts_table_session_set_ssl_protocol(session, "TLS"); + ts_table_session_set_trust_store(session, example_tls_trust_store_path(), kStorePassword); +} + +namespace examplessl { + +void configureTreeSessionBuilder(SessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(trustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureTableSessionBuilder(TableSessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(trustStorePath()) + ->trustStorePwd(kStorePassword); +} + +} // namespace examplessl diff --git a/iotdb-client/client-cpp/examples/ExampleTlsConfig.h b/iotdb-client/client-cpp/examples/ExampleTlsConfig.h new file mode 100644 index 000000000000..8473d218bcaa --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleTlsConfig.h @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_EXAMPLE_TLS_CONFIG_H +#define IOTDB_EXAMPLE_TLS_CONFIG_H + +#include "SessionC.h" + +#ifdef __cplusplus +#include "Session.h" +#include "SessionBuilder.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** One-way TLS trust store used by TLS examples (PKCS12 under fixtures/tls/). */ +const char* example_tls_trust_store_path(void); + +void example_tls_configure_tree_session(CSession* session); +void example_tls_configure_table_session(CTableSession* session); + +#ifdef __cplusplus +} + +namespace examplessl { + +void configureTreeSessionBuilder(SessionBuilder& builder); +void configureTableSessionBuilder(TableSessionBuilder& builder); + +} // namespace examplessl +#endif + +#endif // IOTDB_EXAMPLE_TLS_CONFIG_H diff --git a/iotdb-client/client-cpp/examples/README.md b/iotdb-client/client-cpp/examples/README.md index 945686d4e2a0..dbace0eba276 100644 --- a/iotdb-client/client-cpp/examples/README.md +++ b/iotdb-client/client-cpp/examples/README.md @@ -32,12 +32,18 @@ user `root` / `root`). | Example | Description | |---------|-------------| -| `SessionExample` | Tree model: DDL, insert, query, delete | -| `AlignedTimeseriesSessionExample` | Aligned time series and templates | -| `TableModelSessionExample` | Table (relational) model | +| `cpp_tree_example` | C++ tree model smoke test (plain RPC) | +| `cpp_table_example` | C++ table model smoke test (plain RPC) | +| `cpp_tls_example` | C++ tree model over one-way TLS | +| `cpp_ntls_example` | C++ TLCP handshake against local openssl NTLS `s_server` | +| `tree_example` | C Session API tree model (plain RPC) | +| `table_example` | C Session API table model (plain RPC) | +| `tls_tree_example` | C Session API tree model over one-way TLS | +| `c_ntls_example` | C TLCP handshake against local openssl NTLS `s_server` | +| `SessionExample` | Full tree-model walkthrough (not run in CI) | +| `AlignedTimeseriesSessionExample` | Aligned time series demo (not run in CI) | +| `TableModelSessionExample` | Full table-model walkthrough (not run in CI) | | `MultiSvrNodeClient` | Multi-node insert/query loop | -| `tree_example` | C Session API (tree model) | -| `table_example` | C Session API (table model) | ## Which SDK zip to use diff --git a/iotdb-client/client-cpp/examples/SessionExample.cpp b/iotdb-client/client-cpp/examples/SessionExample.cpp index 9b429b0189b3..1546204359e0 100644 --- a/iotdb-client/client-cpp/examples/SessionExample.cpp +++ b/iotdb-client/client-cpp/examples/SessionExample.cpp @@ -452,6 +452,7 @@ int main() { session->close(); delete session; + session = nullptr; cout << "finished!\n" << endl; return 0; diff --git a/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp b/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp index 3ae321e18f88..ef1195b167c1 100644 --- a/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp +++ b/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp @@ -24,6 +24,10 @@ using namespace std; shared_ptr session; +static void configureTableBuilder(TableSessionBuilder& builder) { + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); +} + void insertRelationalTablet() { vector> schemaList{ @@ -86,12 +90,9 @@ void OutputWithType(unique_ptr& dataSet) { int main() { try { - session = (new TableSessionBuilder()) - ->host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->build(); + TableSessionBuilder builder; + configureTableBuilder(builder); + session = builder.build(); cout << "[Create Database db1,db2]\n" << endl; try { @@ -156,14 +157,10 @@ int main() { session->close(); - // specify database in constructor - session = (new TableSessionBuilder()) - ->host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->database("db1") - ->build(); + TableSessionBuilder builder2; + configureTableBuilder(builder2); + builder2.database("db1"); + session = builder2.build(); cout << "[Show tables from current database(db1)]\n" << endl; try { diff --git a/iotdb-client/client-cpp/examples/c_ntls_example.c b/iotdb-client/client-cpp/examples/c_ntls_example.c new file mode 100644 index 000000000000..b131a1009231 --- /dev/null +++ b/iotdb-client/client-cpp/examples/c_ntls_example.c @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "ExampleNtlsHandshake.h" + +int main(void) { + if (example_run_tlcp_handshake() != 0) { + fprintf(stderr, "[c_ntls_example] TLCP handshake failed\n"); + return 1; + } + printf("[c_ntls_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp b/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp new file mode 100644 index 000000000000..8a0cd37f1f0d --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "ExampleNtlsHandshake.h" + +int main(void) { + if (example_run_tlcp_handshake() != 0) { + fprintf(stderr, "[cpp_ntls_example] TLCP handshake failed\n"); + return 1; + } + printf("[cpp_ntls_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_table_example.cpp b/iotdb-client/client-cpp/examples/cpp_table_example.cpp new file mode 100644 index 000000000000..f38fa9d46abd --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_table_example.cpp @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "SessionDataSet.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" + +int main() { + TableSessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + std::shared_ptr session = builder.build(); + session->open(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_demo_table"); + session->executeNonQueryStatement("CREATE DATABASE cpp_demo_table"); + session->executeNonQueryStatement("USE cpp_demo_table"); + session->executeNonQueryStatement( + "CREATE TABLE IF NOT EXISTS demo_t (tag1 STRING TAG, value INT32 FIELD)"); + session->executeNonQueryStatement("INSERT INTO demo_t(time, tag1, value) VALUES (1, 'a', 42)"); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT time, value FROM demo_t WHERE tag1 = 'a'")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_table_example] expected one row\n"; + return 1; + } + std::shared_ptr record = dataSet->next(); + if (record->fields[1].intV.value() != 42) { + std::cerr << "[cpp_table_example] unexpected value\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_demo_table"); + session->close(); + std::cout << "[cpp_table_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_tls_example.cpp b/iotdb-client/client-cpp/examples/cpp_tls_example.cpp new file mode 100644 index 000000000000..1b5c3e9d68ce --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_tls_example.cpp @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "ExampleTlsConfig.h" +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionDataSet.h" + +int main() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + examplessl::configureTreeSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(false); + + const std::string database = "root.cpp_demo_tls"; + const std::string timeseries = database + ".d0.s0"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, CompressionType::UNCOMPRESSED); + session->insertRecord(database + ".d0", 1, {"s0"}, {"7"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s0 FROM " + database + ".d0")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_tls_example] expected one row\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); + std::cout << "[cpp_tls_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_tree_example.cpp b/iotdb-client/client-cpp/examples/cpp_tree_example.cpp new file mode 100644 index 000000000000..2f594b3f227b --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_tree_example.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionDataSet.h" + +int main() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + std::shared_ptr session = builder.build(); + session->open(false); + + const std::string database = "root.cpp_demo_tree"; + const std::string timeseries = database + ".d0.s0"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT64, TSEncoding::RLE, CompressionType::SNAPPY); + session->insertRecord(database + ".d0", 1, {"s0"}, {"100"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s0 FROM " + database + ".d0")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_tree_example] expected one row\n"; + return 1; + } + std::shared_ptr record = dataSet->next(); + if (record->fields[0].longV.value() != 100) { + std::cerr << "[cpp_tree_example] unexpected value\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); + std::cout << "[cpp_tree_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/tls_tree_example.c b/iotdb-client/client-cpp/examples/tls_tree_example.c new file mode 100644 index 000000000000..edd44a250799 --- /dev/null +++ b/iotdb-client/client-cpp/examples/tls_tree_example.c @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include "ExampleTlsConfig.h" +#include "SessionC.h" + +#define HOST "127.0.0.1" +#define PORT 6667 +#define USER "root" +#define PASS "root" +#define TS_PATH "root.cdemo_tls.d0.s0" +#define DEVICE "root.cdemo_tls.d0" + +static void fail(const char* ctx, CSession* s) { + fprintf(stderr, "[tls_tree_example] %s failed: %s\n", ctx, ts_get_last_error()); + if (s) { + ts_session_close(s); + ts_session_destroy(s); + } + exit(1); +} + +int main(void) { + CSession* session = ts_session_new(HOST, PORT, USER, PASS); + if (!session) { + fprintf(stderr, "[tls_tree_example] ts_session_new returned NULL\n"); + return 1; + } + example_tls_configure_tree_session(session); + if (ts_session_open(session) != TS_OK) { + fail("ts_session_open", session); + } + + bool exists = false; + if (ts_session_check_timeseries_exists(session, TS_PATH, &exists) != TS_OK) { + fail("ts_session_check_timeseries_exists", session); + } + if (exists) { + (void)ts_session_delete_timeseries(session, TS_PATH); + } + if (ts_session_create_timeseries(session, TS_PATH, TS_TYPE_INT64, TS_ENCODING_RLE, + TS_COMPRESSION_SNAPPY) != TS_OK) { + fail("ts_session_create_timeseries", session); + } + + const char* measurements[] = {"s0"}; + const char* values[] = {"100"}; + if (ts_session_insert_record_str(session, DEVICE, 1LL, 1, measurements, values) != TS_OK) { + fail("ts_session_insert_record_str", session); + } + + CSessionDataSet* dataSet = NULL; + if (ts_session_execute_query(session, "select s0 from root.cdemo_tls.d0", &dataSet) != TS_OK) { + fail("ts_session_execute_query", session); + } + if (!dataSet || !ts_dataset_has_next(dataSet)) { + fail("ts_session_execute_query empty", session); + } + ts_dataset_destroy(dataSet); + + (void)ts_session_delete_timeseries(session, TS_PATH); + ts_session_close(session); + ts_session_destroy(session); + printf("[tls_tree_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index fe92f791fb74..978561904767 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -64,6 +64,7 @@ ${os.classifier} iotdb-session-cpp-${project.version}-${client.cpp.package.classifier} ${env.GITHUB_RUN_ID} + ${project.basedir}/../../distribution/target/apache-iotdb-${project.version}-all-bin/apache-iotdb-${project.version}-all-bin @@ -141,16 +142,56 @@ + cmake-run-test test + none + + + + + org.codehaus.mojo + exec-maven-plugin + + + configure-iotdb-plain-it + + exec + + pre-integration-test + + ${ctest.skip.tests} + python + + ${project.basedir}/test/scripts/configure_iotdb_ssl_it.py + ${iotdb.dist.root} + ${project.basedir}/test/fixtures + disable + + + + + run-cpp-it-phases + + exec + integration-test - ${cmake.build.type} - ${cmake.project.dir} - ${maven.test.skip} + ${ctest.skip.tests} + python + + ${project.basedir}/test/scripts/run_cpp_it_phases.py + ${cmake.project.dir} + ${iotdb.dist.root} + ${project.basedir}/test/fixtures + ${project.basedir}/test/scripts + ${iotdb.start.script} + --config + ${cmake.build.type} + diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index c147bb7cab34..16c8a0e52a37 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -38,12 +38,15 @@ if(NOT EXISTS "${_catch2_header}") file(DOWNLOAD "${CATCH2_URL}" "${_catch2_header}" SHOW_PROGRESS TLS_VERIFY ON) endif() -set(_test_targets +set(_plain_test_targets session_tests session_relational_tests session_c_tests - session_c_relational_tests - rpc_ssl_utils_tests) + session_c_relational_tests) + +set(_rpc_ssl_test_targets rpc_ssl_utils_tests rpc_ntls_utils_tests) + +set(_test_targets ${_plain_test_targets} ${_rpc_ssl_test_targets}) add_executable(session_tests main.cpp cpp/sessionIT.cpp) add_executable(session_relational_tests main_Relational.cpp cpp/sessionRelationalIT.cpp) @@ -52,12 +55,20 @@ add_executable(session_c_relational_tests main_c_Relational.cpp cpp/sessionCRela add_executable(rpc_ssl_utils_tests main_rpc_ssl.cpp cpp/RpcSslUtilsTest.cpp - cpp/RpcSslMutualAuthTest.cpp + cpp/RpcSslTlsMutualAuthTest.cpp + cpp/RpcSslIotdbE2eTest.cpp + cpp/SslTestFixtures.cpp + cpp/ItSslConnection.cpp) +add_executable(rpc_ntls_utils_tests + main_rpc_ntls.cpp + cpp/RpcSslTlcpMutualAuthTest.cpp + cpp/RpcNtlsE2eTest.cpp cpp/SslTestFixtures.cpp) foreach(_t IN LISTS _test_targets) target_include_directories(${_t} PRIVATE "${_catch2_include_dir}" + "${CMAKE_CURRENT_SOURCE_DIR}/cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/rpc" "${THRIFT_GEN_CPP_DIR}" "${THRIFT_INCLUDE_DIR}") @@ -71,6 +82,8 @@ foreach(_t IN LISTS _test_targets) endforeach() if(WITH_SSL) + target_compile_definitions(rpc_ssl_utils_tests PRIVATE IOTDB_RPC_SSL_IT=1) + if(WIN32) set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl.exe") else() @@ -82,25 +95,35 @@ if(WITH_SSL) string(REPLACE "\\" "/" _iotdb_test_fixtures_dir_fwd "${_iotdb_test_fixtures_dir}") string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir}") + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_compile_definitions(${_t} PRIVATE + IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir_fwd}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" + "$/fixtures" + COMMENT "Copy SSL test fixtures next to ${_t}") + endforeach() target_compile_definitions(rpc_ssl_utils_tests PRIVATE - IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir_fwd}" IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") - add_custom_command(TARGET rpc_ssl_utils_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" - "$/fixtures" - COMMENT "Copy SSL test fixtures next to rpc_ssl_utils_tests") + target_compile_definitions(rpc_ntls_utils_tests PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") if(WIN32) - target_link_libraries(rpc_ssl_utils_tests PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + endforeach() else() - target_link_libraries(rpc_ssl_utils_tests PRIVATE iotdb_thrift_static) + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_link_libraries(${_t} PRIVATE iotdb_thrift_static) + endforeach() endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) set(_iotdb_asan_targets session_tests session_relational_tests session_c_tests - session_c_relational_tests rpc_ssl_utils_tests) + session_c_relational_tests rpc_ssl_utils_tests rpc_ntls_utils_tests) foreach(_t IN LISTS _iotdb_asan_targets) target_compile_options(${_t} PRIVATE -fsanitize=address -fno-omit-frame-pointer) target_link_options(${_t} PRIVATE -fsanitize=address) @@ -123,11 +146,12 @@ if(MSVC) add_test(NAME sessionCIT CONFIGURATIONS Release COMMAND session_c_tests) add_test(NAME sessionCRelationalIT CONFIGURATIONS Release COMMAND session_c_relational_tests) add_test(NAME rpcSslUtilsTest CONFIGURATIONS Release COMMAND rpc_ssl_utils_tests) + add_test(NAME rpcNtlsUtilsTest CONFIGURATIONS Release COMMAND rpc_ntls_utils_tests) foreach(_t IN LISTS _test_targets) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) - if(WITH_SSL) + if(WITH_SSL AND ${_t} IN_LIST _rpc_ssl_test_targets) _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls) foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls) add_custom_command(TARGET ${_t} POST_BUILD @@ -143,12 +167,13 @@ else() add_test(NAME sessionCIT COMMAND session_c_tests) add_test(NAME sessionCRelationalIT COMMAND session_c_relational_tests) add_test(NAME rpcSslUtilsTest COMMAND rpc_ssl_utils_tests) + add_test(NAME rpcNtlsUtilsTest COMMAND rpc_ntls_utils_tests) foreach(_t IN LISTS _test_targets) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMENT "Copy IoTDB runtime library next to ${_t}") - if(WITH_SSL) + if(WITH_SSL AND ${_t} IN_LIST _rpc_ssl_test_targets) foreach(_ssl_lib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY) if(_ssl_lib AND EXISTS "${_ssl_lib}") add_custom_command(TARGET ${_t} POST_BUILD @@ -161,7 +186,12 @@ else() endforeach() endif() -# Run sequentially: parallel ctest overloads the single local IoTDB instance. set_tests_properties( - sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT rpcSslUtilsTest - PROPERTIES RUN_SERIAL TRUE) + sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT + PROPERTIES LABELS "plain" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +set_tests_properties( + rpcSslUtilsTest + PROPERTIES LABELS "ssl" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +set_tests_properties( + rpcNtlsUtilsTest + PROPERTIES LABELS "ntls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp new file mode 100644 index 000000000000..4b9f15e76b33 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp @@ -0,0 +1,180 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ItSslConnection.h" + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) + +#include +#include + +#include "SessionPool.h" + +#if defined(_WIN32) +#include +#else +#include +#endif + +namespace { + +constexpr const char* kStorePassword = "thrift"; + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string fixturesRoot() { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + if (pathExists(joinPath(configured, "tls/tls-trust.p12")) || + pathExists(joinPath(configured, "tls\\tls-trust.p12"))) { + return configured; + } +#endif + const std::string copied = joinPath(executableDir(), "fixtures"); + if (pathExists(joinPath(copied, "tls/tls-trust.p12")) || + pathExists(joinPath(copied, "tls\\tls-trust.p12"))) { + return copied; + } +#ifdef IOTDB_TEST_FIXTURES_DIR + return IOTDB_TEST_FIXTURES_DIR; +#else + return copied; +#endif +} + +std::string tlsTrustStorePath() { + static const std::string path = joinPath(joinPath(fixturesRoot(), "tls"), "tls-trust.p12"); + return path; +} + +} // namespace + +void it_ssl_configure_tree_session(CSession* session) { + if (session == nullptr) { + return; + } + ts_session_set_use_ssl(session, true); + ts_session_set_ssl_protocol(session, "TLS"); + ts_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); +} + +void it_ssl_configure_table_session(CTableSession* session) { + if (session == nullptr) { + return; + } + ts_table_session_set_use_ssl(session, true); + ts_table_session_set_ssl_protocol(session, "TLS"); + ts_table_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); +} + +namespace itssl { + +void configureSessionBuilder(SessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureTableSessionBuilder(TableSessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureSessionPoolBuilder(SessionPoolBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +std::shared_ptr newOpenedTreeSession() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + configureSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(false); + return session; +} + +std::shared_ptr newOpenedTableSession() { + TableSessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + configureTableSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(); + return session; +} + +} // namespace itssl + +#else // WITH_SSL && IOTDB_RPC_SSL_IT + +void it_ssl_configure_tree_session(CSession* session) { + (void)session; +} + +void it_ssl_configure_table_session(CTableSession* session) { + (void)session; +} + +#endif // WITH_SSL && IOTDB_RPC_SSL_IT diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.h b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h new file mode 100644 index 000000000000..9818fd076439 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_IT_SSL_CONNECTION_H +#define IOTDB_IT_SSL_CONNECTION_H + +#include "SessionC.h" + +#ifdef __cplusplus +#include + +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionPool.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** Apply one-way TLS settings for integration tests against a TLS-enabled IoTDB. */ +void it_ssl_configure_tree_session(CSession* session); +void it_ssl_configure_table_session(CTableSession* session); + +#ifdef __cplusplus +} + +namespace itssl { + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) +void configureSessionBuilder(SessionBuilder& builder); +void configureTableSessionBuilder(TableSessionBuilder& builder); +void configureSessionPoolBuilder(SessionPoolBuilder& builder); +std::shared_ptr newOpenedTreeSession(); +std::shared_ptr newOpenedTableSession(); +#endif + +} // namespace itssl +#endif + +#endif // IOTDB_IT_SSL_CONNECTION_H diff --git a/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp new file mode 100644 index 000000000000..9bf6558f6e23 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include +#include +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +SslConfig tlcpTrustOnlyConfig() { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + return config; +} + +SslConfig tlcpMutualConfig() { + SslConfig config = tlcpTrustOnlyConfig(); + config.keyStore = ssltest::buildTlcpDualKeyStoreP12(); + config.keyStorePwd = ssltest::kStorePassword; + return config; +} + +bool startTlcpServer(ssltest::OpenSslServerProcess& server, bool requireClientCert) { + const std::string caFile = ssltest::tlcpFixture("ca.crt"); + const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); + const std::string signKey = ssltest::tlcpFixture("server_sign.key"); + const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); + const std::string encKey = ssltest::tlcpFixture("server_enc.key"); + if (!fixtureExists(caFile) || !fixtureExists(signCert) || !fixtureExists(signKey) || + !fixtureExists(encCert) || !fixtureExists(encKey)) { + return false; + } + + std::vector args = { + "-enable_ntls", + "-ntls", + "-CAfile", caFile, + "-sign_cert", signCert, + "-sign_key", signKey, + "-enc_cert", encCert, + "-enc_key", encKey, + "-www", + }; + if (requireClientCert) { + args.push_back("-Verify"); + args.push_back("1"); + } + return server.start(args) && server.running() && server.port() > 0; +} + +} // namespace + +TEST_CASE("TLCP one-way handshake with openssl NTLS s_server", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, false)); + REQUIRE(ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLCP one-way auth fails when server requires client certificate", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, true)); + REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLCP mutual auth handshake with dual PKCS12 client store", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, true)); + const SslConfig config = tlcpMutualConfig(); + REQUIRE_FALSE(config.keyStore.empty()); + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); + server.stop(); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp new file mode 100644 index 000000000000..ef77b53034b6 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp @@ -0,0 +1,175 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include +#include + +#include "Common.h" +#include "ItSslConnection.h" +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionC.h" +#include "SessionDataSet.h" +#include "SslTestFixtures.h" +#include "TableSessionBuilder.h" + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) + +TEST_CASE("TLS tree Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb][e2e]") { + auto session = itssl::newOpenedTreeSession(); + REQUIRE(session != nullptr); + + const std::string database = "root.cpp_ssl_it_tree"; + const std::string timeseries = database + ".d1.s1"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, CompressionType::UNCOMPRESSED); + session->insertRecord(database + ".d1", 1, {"s1"}, {"1"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s1 FROM " + database + ".d1")); + REQUIRE(dataSet != nullptr); + REQUIRE(dataSet->hasNext()); + std::shared_ptr record = dataSet->next(); + REQUIRE(record != nullptr); + REQUIRE(record->timestamp == 1); + REQUIRE(record->fields.size() == 1); + REQUIRE(record->fields[0].intV.value() == 1); + REQUIRE_FALSE(dataSet->hasNext()); + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); +} + +TEST_CASE("TLS table Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb][e2e]") { + auto session = itssl::newOpenedTableSession(); + REQUIRE(session != nullptr); + + session->executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS cpp_ssl_it_table"); + session->executeNonQueryStatement("USE cpp_ssl_it_table"); + session->executeNonQueryStatement( + "CREATE TABLE IF NOT EXISTS ssl_it_table (tag1 STRING TAG, value INT32 FIELD)"); + session->executeNonQueryStatement("INSERT INTO ssl_it_table(time, tag1, value) VALUES (1, 't1', 42)"); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT time, value FROM ssl_it_table WHERE tag1 = 't1'")); + REQUIRE(dataSet != nullptr); + REQUIRE(dataSet->hasNext()); + std::shared_ptr record = dataSet->next(); + REQUIRE(record != nullptr); + REQUIRE(record->fields.size() == 2); + REQUIRE(record->fields[0].longV.value() == 1); + REQUIRE(record->fields[1].intV.value() == 42); + REQUIRE_FALSE(dataSet->hasNext()); + dataSet->closeOperationHandle(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_ssl_it_table"); + session->close(); +} + +TEST_CASE("TLS C tree Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { + CSession* session = ts_session_new("127.0.0.1", 6667, "root", "root"); + REQUIRE(session != nullptr); + it_ssl_configure_tree_session(session); + REQUIRE(ts_session_open(session) == TS_OK); + + const char* path = "root.cpp_ssl_it_c.d1.s1"; + bool exists = false; + REQUIRE(ts_session_check_timeseries_exists(session, path, &exists) == TS_OK); + if (exists) { + REQUIRE(ts_session_delete_timeseries(session, path) == TS_OK); + } + + REQUIRE(ts_session_create_database(session, "root.cpp_ssl_it_c") == TS_OK); + REQUIRE(ts_session_create_timeseries(session, path, TS_TYPE_INT32, TS_ENCODING_PLAIN, + TS_COMPRESSION_UNCOMPRESSED) == TS_OK); + const char* measurements[] = {"s1"}; + const char* values[] = {"1"}; + REQUIRE(ts_session_insert_record_str(session, "root.cpp_ssl_it_c.d1", 1, 1, measurements, values) == + TS_OK); + + CSessionDataSet* dataSet = nullptr; + REQUIRE(ts_session_execute_query(session, "SELECT s1 FROM root.cpp_ssl_it_c.d1", &dataSet) == TS_OK); + REQUIRE(dataSet != nullptr); + REQUIRE(ts_dataset_has_next(dataSet)); + CRowRecord* record = ts_dataset_next(dataSet); + REQUIRE(record != nullptr); + REQUIRE(ts_row_record_get_timestamp(record) == 1); + REQUIRE(ts_row_record_get_field_count(record) == 1); + REQUIRE(ts_row_record_get_int32(record, 0) == 1); + ts_row_record_destroy(record); + REQUIRE_FALSE(ts_dataset_has_next(dataSet)); + ts_dataset_destroy(dataSet); + + REQUIRE(ts_session_delete_timeseries(session, path) == TS_OK); + REQUIRE(ts_session_delete_database(session, "root.cpp_ssl_it_c") == TS_OK); + REQUIRE(ts_session_close(session) == TS_OK); + ts_session_destroy(session); +} + +TEST_CASE("TLS C table Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { + CTableSession* session = ts_table_session_new("127.0.0.1", 6667, "root", "root", ""); + REQUIRE(session != nullptr); + it_ssl_configure_table_session(session); + REQUIRE(ts_table_session_open(session) == TS_OK); + + REQUIRE(ts_table_session_execute_non_query(session, "CREATE DATABASE IF NOT EXISTS cpp_ssl_it_c_table") == + TS_OK); + REQUIRE(ts_table_session_execute_non_query(session, "USE cpp_ssl_it_c_table") == TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, + "CREATE TABLE IF NOT EXISTS ssl_it_c_table (tag1 STRING TAG, value INT32 FIELD)") == TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, "INSERT INTO ssl_it_c_table(time, tag1, value) VALUES (1, 't1', 42)") == TS_OK); + + CSessionDataSet* dataSet = nullptr; + REQUIRE(ts_table_session_execute_query( + session, "SELECT time, value FROM ssl_it_c_table WHERE tag1 = 't1'", &dataSet) == TS_OK); + REQUIRE(dataSet != nullptr); + REQUIRE(ts_dataset_has_next(dataSet)); + CRowRecord* record = ts_dataset_next(dataSet); + REQUIRE(record != nullptr); + REQUIRE(ts_row_record_get_field_count(record) >= 2); + REQUIRE(ts_row_record_get_int64(record, 0) == 1); + REQUIRE(ts_row_record_get_int32(record, 1) == 42); + ts_row_record_destroy(record); + ts_dataset_destroy(dataSet); + + REQUIRE(ts_table_session_execute_non_query(session, "DROP DATABASE IF EXISTS cpp_ssl_it_c_table") == TS_OK); + REQUIRE(ts_table_session_close(session) == TS_OK); + ts_table_session_destroy(session); +} + +TEST_CASE("Plain client cannot connect to TLS-enabled IoTDB", "[rpc][ssl][iotdb][e2e]") { + Session session("127.0.0.1", 6667, "root", "root"); + REQUIRE_THROWS_AS(session.open(false), IoTDBException); +} + +#endif // WITH_SSL && IOTDB_RPC_SSL_IT diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp new file mode 100644 index 000000000000..f749d661b421 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl][mutual]") { +#if WITH_SSL + const std::string trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + REQUIRE(fixtureExists(trustStore)); + const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); + REQUIRE_FALSE(keyStore.empty()); + REQUIRE(fixtureExists(keyStore)); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = trustStore; + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); + REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp similarity index 63% rename from iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp rename to iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp index 53e7f70ea81f..309327f0272c 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslMutualAuthTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp @@ -59,31 +59,6 @@ TEST_CASE("TLS mutual auth creates client SSL_CTX with trust and key stores", "[ #endif } -TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl][mutual]") { -#if WITH_SSL - const std::string trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); - REQUIRE(fixtureExists(trustStore)); - const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); - REQUIRE_FALSE(keyStore.empty()); - REQUIRE(fixtureExists(keyStore)); - - SslConfig config; - config.useSsl = true; - config.sslProtocol = "TLCP"; - config.trustStore = trustStore; - config.trustStorePwd = ssltest::kStorePassword; - config.keyStore = keyStore; - config.keyStorePwd = ssltest::kStorePassword; - - REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); - REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); - - SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); - REQUIRE(ctx != nullptr); - SSL_CTX_free(ctx); -#endif -} - TEST_CASE("TLS mutual auth handshake with openssl s_server", "[rpc][ssl][mutual][e2e]") { #if WITH_SSL const std::string caFile = ssltest::tlsFixture("ca.crt"); @@ -147,47 +122,3 @@ TEST_CASE("TLS one-way auth fails when server requires client certificate", "[rp server.stop(); #endif } - -TEST_CASE("TLCP mutual auth handshake with openssl NTLS s_server", "[rpc][ssl][mutual][e2e]") { -#if WITH_SSL - const std::string caFile = ssltest::tlcpFixture("ca.crt"); - const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); - const std::string signKey = ssltest::tlcpFixture("server_sign.key"); - const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); - const std::string encKey = ssltest::tlcpFixture("server_enc.key"); - REQUIRE(fixtureExists(caFile)); - REQUIRE(fixtureExists(signCert)); - REQUIRE(fixtureExists(signKey)); - REQUIRE(fixtureExists(encCert)); - REQUIRE(fixtureExists(encKey)); - - ssltest::OpenSslServerProcess server; - const bool started = server.start({ - "-enable_ntls", - "-ntls", - "-Verify", "1", - "-CAfile", caFile, - "-sign_cert", signCert, - "-sign_key", signKey, - "-enc_cert", encCert, - "-enc_key", encKey, - "-www", - }); - REQUIRE(started); - REQUIRE(server.running()); - - const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); - REQUIRE_FALSE(keyStore.empty()); - - SslConfig config; - config.useSsl = true; - config.sslProtocol = "TLCP"; - config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); - config.trustStorePwd = ssltest::kStorePassword; - config.keyStore = keyStore; - config.keyStorePwd = ssltest::kStorePassword; - - REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); - server.stop(); -#endif -} diff --git a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp index 584fe1d6c8fb..0eb9512f0a1c 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp @@ -19,6 +19,7 @@ #include "catch.hpp" #include "SessionC.h" +#include "SessionC.h" #include #include #include diff --git a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp index 3b19f2e2b25d..52c34bd73416 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp @@ -91,7 +91,7 @@ TEST_CASE("Test Session constructor with nodeUrls", "[SessionInitAndOperate]") { std::vector nodeUrls = {"127.0.0.1:6667"}; std::shared_ptr localSession = std::make_shared(nodeUrls, "root", "root"); - localSession->open(); + localSession->open(false); if (!localSession->checkTimeseriesExists("root.test.d1.s1")) { localSession->createTimeseries("root.test.d1.s1", TSDataType::INT64, TSEncoding::RLE, CompressionType::SNAPPY); @@ -106,9 +106,9 @@ TEST_CASE("Test Session builder with nodeUrls", "[SessionBuilderInit]") { std::vector nodeUrls = {"127.0.0.1:6667"}; auto builder = std::unique_ptr(new SessionBuilder()); - std::shared_ptr session = std::shared_ptr( - builder->username("root")->password("root")->nodeUrls(nodeUrls)->build()); - session->open(); + builder->username("root")->password("root")->nodeUrls(nodeUrls); + std::shared_ptr session = builder->build(); + session->open(false); if (!session->checkTimeseriesExists("root.test.d1.s1")) { session->createTimeseries("root.test.d1.s1", TSDataType::INT64, TSEncoding::RLE, CompressionType::SNAPPY); @@ -386,9 +386,9 @@ TEST_CASE("Tablet index bounds", "[tabletBounds]") { TEST_CASE("Session rejects SQL after close", "[sessionClose]") { CaseReporter cr("sessionClose"); SessionBuilder builder; - auto localSession = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); - localSession->open(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + auto localSession = builder.build(); + localSession->open(false); localSession->close(); REQUIRE_THROWS_AS(localSession->executeNonQueryStatement("show databases"), IoTDBConnectionException); @@ -933,13 +933,13 @@ TEST_CASE("Numeric column widening getters align with Java TsFile", "[column]") } TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool]") { CaseReporter cr("SessionPool basic"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(3) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(3); + auto pool = poolBuilder.build(); { PooledSession s = pool->getSession(); @@ -975,13 +975,13 @@ TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool] TEST_CASE("SessionPool is safe under concurrent writers", "[sessionPool]") { CaseReporter cr("SessionPool concurrency"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(4) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(4); + auto pool = poolBuilder.build(); { PooledSession s = pool->getSession(); @@ -1040,14 +1040,14 @@ TEST_CASE("SessionPool is safe under concurrent writers", "[sessionPool]") { TEST_CASE("SessionPool getSession times out when exhausted", "[sessionPool]") { CaseReporter cr("SessionPool exhaustion timeout"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(1) - ->waitToGetSessionTimeoutMs(200) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(1) + ->waitToGetSessionTimeoutMs(200); + auto pool = poolBuilder.build(); PooledSession held = pool->getSession(); REQUIRE(static_cast(held)); diff --git a/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp index 9ed3d334b1c4..f3c5aaf32c49 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp @@ -74,8 +74,8 @@ TEST_CASE("Test TableSession builder with nodeUrls", "[SessionBuilderInit]") { std::vector nodeUrls = {"127.0.0.1:6667"}; auto builder = std::unique_ptr(new TableSessionBuilder()); - std::shared_ptr session = std::shared_ptr( - builder->username("root")->password("root")->nodeUrls(nodeUrls)->build()); + builder->username("root")->password("root")->nodeUrls(nodeUrls); + std::shared_ptr session = builder->build(); session->open(); session->executeNonQueryStatement("DROP DATABASE IF EXISTS db1"); @@ -90,8 +90,8 @@ TEST_CASE("TableSession rejects SQL after close", "[tableSessionClose]") { CaseReporter cr("tableSessionClose"); TableSessionBuilder builder; - auto localSession = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + auto localSession = builder.build(); localSession->open(); localSession->close(); diff --git a/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd index 24fd2303df71..713ace0a2c4a 100644 --- a/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd +++ b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd @@ -41,8 +41,8 @@ echo [fixtures] generating TLS RSA fixtures... "%OPENSSL%" genrsa -out "%TLS_DIR%\ca.key" 2048 "%OPENSSL%" req -new -x509 -days 3650 -key "%TLS_DIR%\ca.key" -out "%TLS_DIR%\ca.crt" -subj "/CN=IoTDB Test CA" "%OPENSSL%" genrsa -out "%TLS_DIR%\server.key" 2048 -"%OPENSSL%" req -new -key "%TLS_DIR%\server.key" -out "%TLS_DIR%\server.csr" -subj "/CN=localhost" -"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\server.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\server.crt" +"%OPENSSL%" req -new -key "%TLS_DIR%\server.key" -out "%TLS_DIR%\server.csr" -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" +"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\server.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\server.crt" -copy_extensions copy "%OPENSSL%" genrsa -out "%TLS_DIR%\client.key" 2048 "%OPENSSL%" req -new -key "%TLS_DIR%\client.key" -out "%TLS_DIR%\client.csr" -subj "/CN=IoTDB Test Client" "%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\client.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\client.crt" diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt index c07b6c18a47c..07ade3a4ced4 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt @@ -1,11 +1,11 @@ -----BEGIN CERTIFICATE----- -MIIBhjCCASugAwIBAgIUG2xUB4pMnW4AbOJ+S907pzNavxUwCgYIKoEcz1UBg3Uw -GDEWMBQGA1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2 -MjkwOTQzNTBaMBgxFjAUBgNVBAMMDUlvVERCIFRMQ1AgQ0EwWTATBgcqhkjOPQIB -BggqgRzPVQGCLQNCAAQtFLNDgh39KkMKMNH2LZBu4dFaSAK1+tTyK7Q+f3sh+hDg -HmT3jsGDqkkshX1dUu2H1rxGhYp2jbp7XH/cOsg1o1MwUTAdBgNVHQ4EFgQUxuRe -jrAZtH5PGdo/JtL1nzzfU0UwHwYDVR0jBBgwFoAUxuRejrAZtH5PGdo/JtL1nzzf -U0UwDwYDVR0TAQH/BAUwAwEB/zAKBggqgRzPVQGDdQNJADBGAiEAh83vSwujQPqQ -LXCoSiPnHndpIMTar2MNH3HvKBRDxJYCIQDeyUdqO12TnmcyqgAevUVzdzbb/mPQ -Opj6J+PCsxyA7Q== +MIIBhTCCASugAwIBAgIUKvwxT3ypjPE0o1Xm4uy26vAV7dEwCgYIKoEcz1UBg3Uw +GDEWMBQGA1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2 +MzAwMzM0MzFaMBgxFjAUBgNVBAMMDUlvVERCIFRMQ1AgQ0EwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAARTre1ea094xClkcp6tz88qakjD3QL3VGQK2OBHWEECG8+v +bCqYUsbcOdNshtjk8MZcpznViFQaS3K+3Bf7FQwzo1MwUTAdBgNVHQ4EFgQUtwWN +1oBD+b/DANRs2So52umc9WEwHwYDVR0jBBgwFoAUtwWN1oBD+b/DANRs2So52umc +9WEwDwYDVR0TAQH/BAUwAwEB/zAKBggqgRzPVQGDdQNIADBFAiEAh8/BGnVxwjuL +yDkaOK/J1IL1c8wIGx6TqW7Re25CkCkCIDtLgej8xmZI4I0nL9Er+YhN8FD4BwzK +qoK4jYsnVf1Z -----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt index 0911322ab0d2..8b3b3a0cddcc 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIBKDCBzgIUPu9HMMReU/2p77newOOfrdaIP3YwCgYIKoEcz1UBg3UwGDEWMBQG -A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz -NTBaMBUxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC -LQNCAASsbddNsVBXsoEv5rs0rPmppN5ahGf7Thsb8AWbj3GwiW2X3Gy7PJGck/kW -ilJP9hGtYpS2Eo/TPXNLqtPcz8DVMAoGCCqBHM9VAYN1A0kAMEYCIQCqYsG+mF14 -adeZf086xrgVHfigfyCL0HdMlx0lxCSLfwIhAOM9yM8ogZ4gGnMlmDdnQLijNbGX -EIjTxunW0kFjg9Ew +MIIBJzCBzgIUWlOhvwyTl25h6Y2n3L//h7bOyXQwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2MzAwMzM0 +MzFaMBUxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAASRqpdiAJcuzGV2xI7NveKK4e/NtlRfnYg4DViomBN4a1sMwYCoz+5hun9S +mlsp/46HmgsHdCfySrMpAapjombnMAoGCCqBHM9VAYN1A0gAMEUCIGjsv/DgrY85 +W0GSyaB0KpFkId0D/s8Vc5hETJw/anC5AiEAyWw7RfrgYrLsSrvyh1rC9xd17jsV +ASgtvkYznvtAuYM= -----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key index e544ad4be49d..a6d75ee094b6 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key @@ -2,7 +2,7 @@ BggqgRzPVQGCLQ== -----END EC PARAMETERS----- -----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg+Jw4VYt3t1WumoqR -dC9UO5tZoQ4SzjeyP9AW4fs5uMuhRANCAASsbddNsVBXsoEv5rs0rPmppN5ahGf7 -Thsb8AWbj3GwiW2X3Gy7PJGck/kWilJP9hGtYpS2Eo/TPXNLqtPcz8DV +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgpM8Gsh1RlMa7+mM7 +rBwXX+zmn3rLR9xrM5CDyXQKvv2hRANCAASRqpdiAJcuzGV2xI7NveKK4e/NtlRf +nYg4DViomBN4a1sMwYCoz+5hun9Smlsp/46HmgsHdCfySrMpAapjombn -----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt index 76c32ae7b1d2..6d64c3cd1783 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIBKDCBzwIUIgMPVhPEAo02eT9LxrqQySqxxckwCgYIKoEcz1UBg3UwGDEWMBQG -A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz -NTBaMBYxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB -gi0DQgAEEmlwmVJKK64nKIVBDMFxcmcWC+EecnWvLOAqanP6d2M9j6sYUtEHX+4o -4gzkw17bzEyeg49INkZpUusKRpTxYDAKBggqgRzPVQGDdQNIADBFAiB6TfpgrH24 -r/HdAgsoiG4ZqJdUcqcPovzmhxXnzlnzgwIhAL86MbJg8MAXe7JQPdca2Hc5VweK -PpkHcpr7inmVwXzZ +MIIBKDCBzwIUJ0vuKfRYbq5vlfVOrnG+BuyB9EIwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2MzAwMzM0 +MzFaMBYxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAEte6Lr50Tithv28OXSI+yewqHNbyGl+5vrgJg93LHzD+wyODruv+bqBF6 +N1KinzdYJrPtQiQOqTR4Zmw32bWVAzAKBggqgRzPVQGDdQNIADBFAiAD7TwAMMdd +r5EmQrDN9v/UGCaQnLOhIL3hoTlgCqR5EwIhAOeOx24taX93GkXWBym//EdUqeJ+ +jPJtJVNVUG5kCtla -----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key index 85c3bd67761b..15537f66dca4 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key @@ -2,7 +2,7 @@ BggqgRzPVQGCLQ== -----END EC PARAMETERS----- -----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgNNBfLhw/OzJefgeG -vt0l1L9i1nI6nTS5QBg6+dy1ulihRANCAAQSaXCZUkorricohUEMwXFyZxYL4R5y -da8s4Cpqc/p3Yz2PqxhS0Qdf7ijiDOTDXtvMTJ6Dj0g2RmlS6wpGlPFg +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQggl/d0rcpSnK+Dawf +D6bzcQvOp4DyCMikGDAeYwY/BnKhRANCAAS17ouvnROK2G/bw5dIj7J7Coc1vIaX +7m+uAmD3csfMP7DI4Ou6/5uoEXo3UqKfN1gms+1CJA6pNHhmbDfZtZUD -----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt index 98377e1a51aa..7e52a1280caa 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIBJzCBzgIUdQacDdq1cFAUNA6808zmxcdkLw4wCgYIKoEcz1UBg3UwGDEWMBQG -A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz -NTBaMBUxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC -LQNCAATBSLlntFGVZdBuYraBVmx/+lcaYeS34+LZQhzZmbRURERpZzEUGYSllWtA -as6u64Ch8Ta8KOBPB/QZiGRdce1BMAoGCCqBHM9VAYN1A0gAMEUCIE8sxrEWDTxR -MLkt/m4CaQgEI8dZN+WnSiYfCknNwT7GAiEAx9IyYLLtzZngErfgV8qDhZ/Ir38D -yJaLPlJHrjH1eVc= +MIIBJzCBzgIUGQOTcoIIr50fdhDjoPyyTTrYga0wCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzJaFw0zNjA2MzAwMzM0 +MzJaMBUxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAARxp2OqYF3uklRiNg5Dz89V/EDsw3uPaXKxqETMs0Jv0AynA/OINtjY1IK2 +jq5eoSIOJKAYV7kIXg8xEAxF1dpQMAoGCCqBHM9VAYN1A0gAMEUCIQC5zmE3XgT6 +qlnFNhUhtk2gTsbC0D0iiVh7oGDHsdV31wIgG/xfFl46bsoXkRdrZTgrDyuSjQ9r +b+SE017fmwAVLUo= -----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key index 8f444121586e..0edfda1a4258 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key @@ -2,7 +2,7 @@ BggqgRzPVQGCLQ== -----END EC PARAMETERS----- -----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5JLkxeJcclsVHu1G -o3LmU2dZCAEfZo6Xed0nL3NCLb6hRANCAATBSLlntFGVZdBuYraBVmx/+lcaYeS3 -4+LZQhzZmbRURERpZzEUGYSllWtAas6u64Ch8Ta8KOBPB/QZiGRdce1B +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg8n5MGmIXrvuybXKW +T/xAMviJsTFOfV/ZSjhcpdrX6PehRANCAARxp2OqYF3uklRiNg5Dz89V/EDsw3uP +aXKxqETMs0Jv0AynA/OINtjY1IK2jq5eoSIOJKAYV7kIXg8xEAxF1dpQ -----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt index 8a0acf250709..a9176c0c62a8 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIBKTCBzwIUbumqE+eQaU19UvFTeUL/l/RCxkswCgYIKoEcz1UBg3UwGDEWMBQG -A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDIwOTQzNTBaFw0zNjA2MjkwOTQz -NTBaMBYxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB -gi0DQgAEDY3huaTyqILrM28nzpcTXVcPWyn0yqqEwdxXWo0uWcmVi5z4o9yDOViJ -0kGNfhbjNkq5O5v2oHLpQxOoopuUbzAKBggqgRzPVQGDdQNJADBGAiEA52Fb/Lxl -k0i2adwbzx9r8UNbtAmWO6IDhGFZZ/o2ljYCIQCC8VplDWGvC7FDPflO2VXZ0wIz -dBztr8q4k+Iu5W/lXQ== +MIIBJzCBzwIURRSH5ItGF6FHEy/mLryngsiS7AMwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzJaFw0zNjA2MzAwMzM0 +MzJaMBYxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAE7eWZ4hSOUnlLO1ZYHGiM+tkYmidfNEIgx/p3bAXB3aWl49WKbA8uMVjI +75QSXvYW1EqYThHRd3Zz2NU1NWBSDDAKBggqgRzPVQGDdQNHADBEAiBbMYyjibY0 +1mPxsDf1KemntnmhTaSukWpyDTu9bdWYKQIgKT9Rsri4T6eCGyeTtU+olCH5S38+ +PHYQ42imyJU6oHw= -----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key index 794fd1e00265..9f546d2f1843 100644 --- a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key @@ -2,7 +2,7 @@ BggqgRzPVQGCLQ== -----END EC PARAMETERS----- -----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgwvH6+d1hrPRPG06J -GPR6La8WweLCNgUfsvpcDa2hAsehRANCAAQNjeG5pPKoguszbyfOlxNdVw9bKfTK -qoTB3FdajS5ZyZWLnPij3IM5WInSQY1+FuM2Srk7m/agculDE6iim5Rv +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg9FrxcDNbCS5UxHuN +zF2fRsR5Xcn82MG1DxwgtcQzc6GhRANCAATt5ZniFI5SeUs7VlgcaIz62RiaJ180 +QiDH+ndsBcHdpaXj1YpsDy4xWMjvlBJe9hbUSphOEdF3dnPY1TU1YFIM -----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 index 39a3eb977ef4cdc45cb83013f68ab5e5f9ed5e85..fcbf1c7c1716732d7749a675ac8e64fb94215fc9 100644 GIT binary patch delta 747 zcmVJ zlaxt!n!RF}(d_8-M`$pA8(sk{KXrDYeli7h_O9L?SR)pY{Zrmt+8S>m&TQnrIWF8A z+{(_q&hmbEe~(0kI*eu_dnUt@P@beLx&w|3k&m+NY4iPY%-|2+E*kM*x_o2u`hQ2( z0(8cHAXTkNeQquTLskdC?Bg>CvV4I`m85Z1x8~fhyQzuyxM5SQB%j8q{%tFeO;%$z zGB=^dw}Z0cu8bT7&S@fY9wd~)*bP=^w@_9yK24jQb(wIIY!soS3xUo=WlFEJH%ff* zwsYh#V_xa{0gDVoJgYKWg8L8Tw|{WflsaRMGQ)~4%!EVBu;fbx+wM9A#&DldIuKa; zN27(KaAIdl;EnA4lkx@j9=tLOc_u4Qt+#QP(OI1+ed^%?G*ljgFP+$>wW#Bw>qxoVD}oj=|9lq#qX!;Q><`Es-#XnHU0?-fm`8 zsAubPyhB8S)y4oD=LH~oeWx*6-yeSlmkS>0tjMG!a@wjm(kB95THSs=r!c8Tms2JI z4kWPfCbB^nA!4FAZ%W$ivUR>9&$zT~{V9>DglN9zR<%)pnf#M>12|WkLbzgJsdmwV z5U{IWxE)-4MExi*K`=2e4F(BdhDZTr0|WvA1povf?toS0z8GL3Vb7+0#A$Bls?4a8 d&8R=rjZT!vUmjR^1PK1w8Q#h*hKB+I2mrysU5Nky delta 747 zcmVUvfl6oJn0r@YkP&Pm@=1jEGymAF7ld*Zo3L#63EXowhZrD(l@Y zvfL0wfhB^$7YQ65wVfy~PM1*&KZI*wSBrZxBDAMzkieCO5y6{+|F^at9|D~IuYV$H zTAqkc-FA%$`j@8ZF@!MgE(8-STC2C!F_};UIIHO>)%^(3o>iG^oR6y^t`FyeW@B%# za)-jvY&aZ-x=GJwkCh{#*`_V=Q`4xd-{EujiUYXvPH{z9;IkIt8;s?CT0x8#sH#e=4GNtoPYGcyA2}1twS>9VPMvKdjYexHB3~PIsjZ@<;1$cnHrt zSwN;Of9icT_jcODsDH3yoB65P3M^*gy4 zKRb#CX`F2jYWRO-n(?;swZ~Jk>|sqyDSp2*XfHZqMJezxfPGrzGLN zH(plo=<4!g8jw6NK`=2e4F(BdhDZTr0|WvA1povfCWRK^2lmT}wrmA%6C5b+NEQP* dQ&b@be+hhmf}zGM1PC2Pja#oX5&!}M2msHWSvCLw diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 index 1eab60c0c6db310abd2cd43934e18411a4d7917d..d82ebeba9be440a47a5efe98d73519098a4f97e2 100644 GIT binary patch delta 744 zcmV{I zN=-l9@LvWo6Ogz16Z`5I;nG%5x}n-|!_yOv-_({YLbj|(v5BJO+#R|;CPOS9 zbPmx!6fRHM`lk@i$*1>Q%gPGyhFjGkV}t^O5_bJDAmO{iZ|>JxIwmq1hajgdKl;76 zAN1j~sMUQ7!sLMPv$5~IYj>q-uzxWSFkE0Ac8g*VC-L@)_CgWjY+)MREV#jjmSC#=4V*Qhmmt3e@XpK;y&dV+TfB%8@jkdAOwMshKUl4 z6y;Bdp9r5S$%RKH7(9A#=Rq5WRB6mc++$F=LRQ!I#uESf)SrLM-2?#(iamPa^hU}g zA}-9b1Gj<6aRNNS!D=tx_+BDOm`vf38E|MD)&uFLbK4#gHvVbs$?*W{JQZn>qa~(m zOV3Ld*K?vYMNos!GpX(pdM`6y5_%7>0Edf=1qerywO$H>IWbHyBL)d7hDe6@4FL%i zF%|?AO6APdYVhJ39>SV!4uu`~F$W5Elbr)RE+@Nr@2?<0|2G}B@;{jGi$@-Jt|m#8 a8_}an9_c_`c?1Y1(hgCg8gF3&0tf)DDOL#p delta 744 zcmVlwv|3_`8UFZ8p!UT+BOn8dME3S70arAZ|;e3B0Dq=e4aLO>KLbJO%A= z1aFB7;mtcbb{!TttQAXKK$y^KvsY6y!sK;|lV?C}yLslB$tWnTl!1FMI-Ua{kS4cE zH=-7_9T?Cdd|g~BJ=YF>U_j;nV}I-E*sPm!z2(GhnkfbP*XkbZesz4KUknR-ek{-n z4$Bd|*2onRy@O$p+$T@D)VjCiM%iJs)xQ^zIJalGUzeyCI9f78NvuB<%lk|tHZ7Tq z)2}iTj-iU7fr8RvV<$$c;;{e-F_HvQ9W3zd3tMteV_W29Z!9-_qdg=1DM#Pp{~#ZK z9{g=A1#){p_#S#H%hg3fPEEYsUj4Gx0Wrbu*w%e@B!-(BT{9#$`8WjizuX@Oi*dVP67L(nl*H(86yqt)eaa{=$n;oxMw38YX)Wu?)O@!RhUf0B|}YcWhPBL)d7hDe6@4FL%i zF%|?A+G)di;vl?7LKZMA%n73&z21jYlbr)RE;01jnuu~9{iNCt?RR~tlG`=P|HUrG a_%Gj}i-#`PlLQDzY7ng)szh@F0tf(Uu2;MO diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 index 95db3ecab34702a905663ec954f31f828e636045..e7322f292e799c0ffb1943b5d72abb1884ea8b57 100644 GIT binary patch delta 558 zcmV+}0@3}e1*-*+YX~|U)gpjztjLjbA%8OGMBFi72))PfzxJSv2TFi~0l+^6YopnC zDocGx0@DMZEFqkN$@S-0w#il#Jb-K0iCP!cG7b7DTmhi}y6XXt4E`1Rh0@Xy+MRQm z0Nu&j+(ZRUl^=}v<&+BL;v7!w=nQ3F>+*SCgI0O8H4*fw2=pe5?s&cKVU!~=9Dkax zmD9*e*-=U>ooov&PvLR8sM`TZna;w@li##9kA1LLETu{LWf=GYm8JL(=EgYD0r@Tf zX5VBTZ3?>_ex_rf0ieyVrV)UtJdPGIG@lQGnlJSX4x!rCU^{oy)QIu)lh~;=5_N4) zdORwSvqI`pEsgBlk2R~sScBy;wtpmBSKKWg^j?#!$1~wU`9d9VY8bXFG5k3$Q0M~j z1~MorPNl$THW$RMm-p?QN|~7p2<>A=-4u|w(fc@f`-bPWFY1MYoesr8tLXNn7>`lU z!-4$QTN=U7&37#JaOZVPX>Qna7FH`)(##08#NkEB$^t<{ORm8RK%p*}@PC;#^e8qN z>S~T-17dN5P~1~*z5w!$2*BhMIS&Tv=8**Ty&_eTRLSB*d0I_Lr39d%Hc@y}sj9!# zD2F*Ig74#A#4POUgF3cMGja&42*6?9E9!NC_b@>)F)$4V31Egu0c8UO0s#d81R%h! w7r2?Mog5$DE`d*i<+6# zggqqWk7;1|jj8(3c5P$VFqdNE92XU*8jcppANAN!D39y_L!Kgef3z-z0}xf0=7048 zT4`HU2EE4Mp5yh6n4Dz%90Aa?{*XD$$DTVR&)rvdC!=$5u1Gka!Ts*4E?O{k9)H$5K7K5XBKq|V8)L49K~a;qv@Tjy@UR_kC_#=H zYZI!CZ-ar{s&_)G%cQ&pQ}-3Kt`zc**&d>O$5JURkh!T-Oa^6S@PpGz&`*GFGPDR6 z2ng0{bJRY>KkNb_)YK)TnV+r<%bcYHsAcIQZdpJ)FEz>MM`;w)hdzvv4}YOVt$*}Q?odx-(jNqxeSr+!_zX4^5$DbJC+j#Q9YaO)Rj z0X(&IF1R~elM_fALsPR$U%7uGCrY|&DTS*V=f6J`s-MxT+8rRu65`gw^Q*mH z_ODT{Yg76n>w=Vcr1?Up8@EBYJvw0(F>|d5v^n`PTE0ib_{|vs`~=Xyj*ku8n^(c9LzO zZm@9wR+D>TrlT0b=yR($Dt0V><8YW(&e6Jf*dMR+PL^n0sya2dJJ8-BM{ zWX(0R3@n3jh`tB8Dd#P5YQ_(|UG(r78Qc!&Cc6MRzRR+av>t-u>(~we>GZ04aJA$< zYR8b6+m+nM^UY#sV>;3|qO z-CIrQ<)hogJBut>Xh<~nu`3?d_rvgZ66S12_$LS+8NCS=U>7s*8_!DwNnyeN&7#gg zFR0g0J+hMZ>*xYy?Q~kXii~v!J@KwhEm$Yj2qmLc>z2RAyU&)@>SqI_&E=9l$4KKE ztAo7xhXGq~%t3r)l&TZManjcQ%^EBJ1;PNV89jjwn5D7+oNyz6!ZKL$326O0pMAQU z6_e-#R0vR4B%_1)l}nRE1R;OQ|Bc56H)actO(eJe;e0*>f&|bjptb8DtN`*Nz7G^n z9DwS-Rdr<=etC3KY8BLqk)!pvU6ew}2IrsWMBzg@>`KO}YE$6AfGM0D3$+Hn<=<2{ z14C_jruu&88|lcSLgA`Tg44*a6<{y(Xd1Z~-BIR1g#4`QUU7wii}-&8NW7BLjM=m3 zrEk)Dy*jYYU`!Zr-i!-NnId3;mXupzKLdkQIqVcZp|si2hRgZ1fe?Uq2!r4g5p|!Z zz2Eb7i*5vKWkVN1Ka4k8mZBTW2HlX*PB4)C4*Pc2sV9|?>M%SY-&DuPGTX%%UnSV| zoj6CQPo&I4;-B5-LwkQ?Go~b)gt@{)+)?Mbs!hlrt>WN1N)i ztiAXbAU#Zv4|)N^z&^&`FHuS#tQP$341FW<$ay~pu-|nFu9imGM_Pe~of!~!*K7Ob z+BOR^TY!@V0wCKmN&ga21}e(TBCQOC6KhLF-zb@(Ab)PXJ7cu$!dvIZ;FDG1=0Q7)k&HAmbZq7(q$$p=2 zr9o7#TL{@ZF_M3hygmTHw-FfrSAgMv-jMY>u(p2~Selb!k3J8>Me639D3BPHT=hJB z8q_@Zcws|WBupj7XJ8HwP1Uv^$x!}j2E>2GEp%jsco!~`&@#7DJ=9$Iljzd#Q?H2g zJ8&V93Z)V?2qD`9Rl6XsP(pX>rh_gzGz*W3h@}{s0S|u)g2ARucvayrXhdm49imVg z%WUeQFv%I!UE+qfdYL?BLlp z(c6EENa}w032<8q+Y=77FmRLkn!N{&x5H--rL~2p^_6Nrj%DX7z+xOap*bbqFVqcl zv=>4_hc$-QsDr>rhbwJe&UjV}q^7cc@_r?%c)004et0tuv|7G-1xBOvmA98gX|fOX zD|DSNqNB8w5n8NP_{+YjpM|>QQr6-1b#Q;J^}=c*mW`f?<29-*olD7)olw!d1G25D zXqi`bD$o^_xQ4z2qGJNuCaV*Z6 zvR+Qf31f48=upK!qiF+wmK1_>&LNQU=}r~?Kmt)PK`=2e4F(BdhDZTr0|WvA1povfzp?KMXsRu_ pANA3bR=*Hw78>QPt|UBqwxumtVdqSL~U`mkZ$UH>DVWf&)P7LjJvz z(Ue2**$|c!a}7oNOPO*zK?C&8C$3Z<#N*I{0>)cRR=V1+;otTjMb#9M z2Qb@lf6WpGNmTt2@*fueOs4J;VxqQB+3A_c0A39^x(Ih;81HCL1qZWjaX0$Y)bb80 z%xqX#p&|-so%_bjW#4aBeD9$&E82g5X7r$>9p9QI3Nm#TMGDgGjutQ%YF`QU*<#d4 zX@w+^VvSmbY;rN6T1PG-QMt^qtQO+istfzDKvYl*dm96RLjUkQ3`lO`E zJIH)AO&!8iF@9o0H;f=j?BqC7iyLZkO0SlhGR0g*+5_UaV_5<*a~P&7B&mM_j}p|h z{{>;{QMFy8ur-&dkR}n8YH7;}d?+$TKHg;RLE!G5Hp^-m_39;uGzF0^5}g;ed!oKr zaJpT!o|5qgf~FjQD?eUzS$mFu&mw?cPxw8|rBQ_YU@YG4T-H{#je5tLm9;yUCP7-y z@2fS;Drl&L2B6a#Cod zc&Cgh>>W7z=gF}Ud-C0uw|rCz0=HkF_wzhsPEy(udJW{+-`8&Ktx$-S%5>j$JzJYi zN8{|^Az}xCTcvC&OENV0Gm^2@9)9S`-q~jjq2sZ8}3>*B=S!QoKDmK@(s~z zSd-`jR0x$2y-wopu;!CQ1R;N+!G35#U`B$@PK*1x(1pDOf&|b2;fHuNLHv;4KIzSs zsRFX1L>|w7kSzY>HFiPIU=5#X)ccSXi1LG^iqhw{O~21*9TF5DAYvt`eCUj>(r_m} z_1{Gt{4u{o#etXFXtsWrO~sKX;)rk6=#1bkwQ`nMzE%2|JgB+r#7uw2L;E^Y7Da?u zekg8L@40OlzYr>JfroO_u@J7RlGcc{*Euw-D6~pCSk4+D+mwl~CbKG0bVn#;eMFu$ zvkqB?%3YX0<}S3r-0a=B{oSy&7?hamv^GnaUJscyIl}Wscw7!}C!K(W08nNF^_`bt z5esf6?4jNHRDjnWJv4t7PESFZx`-t$#yP!}ikW!n=$goK&65^Cxel*cmJtB+A7=9g z+iLl+CF7Ug5U;Rsd!0ni7fF~869Ic&{YK65rLgb6d_R1xA9ktrLZq zrY+iK8ZkX|D*|wvA_iw&i*1Y*tJl6BVFTO^@aIp{-#g^L5y2YgNOY(5Y6%$LZ=Vp+ZOV!+!YSo_M{N%9-1tsvN)T*07_f7ZZ7 zjG@tV?^=nPKS_Vls_&8PeYj@TfYn^sH|cLd%q1=}CLYYRTN{7(+I6d3-4GAnb&*Ko zh=puR$X2|(N5zG0-;2d{nNE}V+nK{?n7}LM{lwuqEIYxWOU@K*JGeEkgoT%O7Rgjx z{NE}nZaKu?Oz$phOa8B;3|eQ3XMG^}IfI$*a-^q{$NPV02J9q>fDnPzPhWIGuw0Tc zneEv~hc}_UO|z)VHFNTTx~_f^i>ioY{Zl$UtcQxzc@$;%-c@^9i^OH+-w3qR45LYy z3w3N|$A4#N@g&Rsw4*;0zEK5O6R9U32e$alzuhj_gqWBiDv)){<+V|I=Uik5@A`nK zKNkF_o1cHLuc`O6)%QyoP<18{`X#V>DL?NT3Zv5r@4~t9q&2T0{pSlB_JZKWEJ=YC zrg#y2S57PK?pdXzVJQskeJbn7WtM1qK%sS5TU2Im43xGRVCr#$Dz%(yu4NmtfUkM? z4(KD&&P10UD+RLPo3np&S)fk9!7;8+$B!8Klfr-F$2RLDJ)p8T97C6q-xfw5ST3f^ zUOi%Q!1%~349Y4%|DIa0b>>PcCC%f|HE=#zU8pN$i;;D118G#|PCG;v9TLH(j^BCK zHB*Fx&6Fnx?myIJrtDh)9X4eyMVX`6SLw1f}$ zf-!%ux4<{~T>&k2>q#;FUWs{&A_g!9uN4Efl&BiU6|lE}QH7ZW+wi-HKWe`i#MkUE zk>kC9WCa(MDU^yuA7r>BpR95gWE=b|(jeorWOqpIT#M8;;S3OP)!#7kbsxAwwQ}UC zO3`Q6K#rTb(QP@wg;OpZ#~{YZU?$5|8^wP<3#ehl?fYywN~DxalRNmku_UJr2-^~g z5UufZ%-BDjmCo}9!bU898wMPmCEDyo1^{`hj0Rom4q)FgA9iD^dTzidOyUXFPlrWC z)A4_od-;WNaM!YcCqi-&3F+wmK1_>&LNQUJ*y~k3X5RhMEODB9BH^B#@iHU8Nyke#ajx`{laF&n<;?yLNfNxXVtNu$ras=3( z6cQQ;eY~@Sp+TU@fASs1W;rrxKDfc8Irq6Hv^tAR*Fb)@pK#j(H?UbRC`YRhKxK;A z+o9yK4}$)K%VWXZh`5{QzhI(qP@EFE(Q;1KS2kTEjL%y7&uQnL)0wzAZAFx}D4WW= z4$izE1p=2t9kk2s%phe)SG_D+9Q_J#o#@E_74X{n|D7%0e-dIqZy0NNk<_RQZsl>% zG8qP2XKZ~AGmM*$`Ct)-HT-J zMr*Uv_o`Z7ab4KxSryF-{XB(T>M{R`HJsGeB9k821&T%)vkqLf9O$_HMJ@IgjL}Yb zpLx`UTvlJjf55hCxn>88SGenizr%c#@vA9l@l~AN3w(YCf3N4DClO5+d~+Zn);9!9 z$KUeF$>5)?oG8b{78AJl6KjepsrMP!9+e^x@FdQmo$bb-G$Mf1YmC7jGX(a|vyP?GiLukvyv|9dma%D;hXsO{hlz(AUMka?AY;ubI zpV&*1mem>q3&^vX2i@0ltKj>hBfe?3)C4O(eg)&=iq1C#d;-n}{ivaeZUOyK1;GDo zUpca1{u~j6HR(x#pllBowXuK?SdJ{>9Q>xJe*rF>mwX=&l0y3C!ZD?ndcW^Q{P8%5 z4lq6v{d+&yW1D~?tpofrtUErp!e~p@={Cxye;E`H9wSw*h{9(i=nLb-oEC;V$ zytL19!gYiguT_xhj^wN4nY~lTz5@2$SiM2C11`zBJN}yXJFloMl6`z@1_{EyEcoeOb4wlh^}P2);%^_e3!b6O%LqA%99lf7yeqf)Be&Cq8e-iLnHN1kitbI1-l) zn|I-iBWxL`4^#9I_y($saXVkBcJ>%-^imGfydSwgK~q}Kn)@ruAC``=vBQGK(euVU z6I(i|4erY6_+!^05U}ZYQvvimiF`keVUdYLDO!Tz`z2i5Yxqy%Y#;Tobio0w)xc%KFBY=7&t+d)AQIED%rwTk|G(N;EGhZ-ERc zQ}g{5fdNLU^6l_12QEvaB2zgc1nlKW4DZ$0eiUEUY_7h^q+#f+o8pEgkIX@#Wr>*~ z09e{Ghy1Y}Q%DNQ{%2CLxF-@yLVr7M4r{PW8Aq5QYmV-AhW$I_INP|yOw!kMxveri z00rm^-d#*JXwc?Cnf~Xmjy$TR{JU6j5`pEf)rJ z0%*nfCAK{I=}FLpSkIDo5=;bzY#nK6YwgwpyI447CwX9p{Ru)iV2-;vY#+ z6pUgpTn0&j%<&LKGJmt^N*q{a;s*J4644A=JSjJ7;(QY&_O_)!XSP#uA6R!AD?CTb z$bP?$e4lMhCq4!2cc3?ZZr3eSDrE4`Q&9-&xn`gCX%SCPDfA9sL9I`IwE?ngI`)8F zQ3L&Po)P^xT4Xb0m_#6v-{RKIW5xa&5Tj#&T)JAvn{DRh7=O`#wIhLLZbHN;;h=w@ z(3|1?V{o(u1W#XwTlQwJC?o0wnMdU3`lDrD$BmutD9SJs$Dz*Mi|;z%^cZrg2V>V& z{7v0HbI1obl&=F#FR7hrqySS?D&z+xG3w)p21oHd#9Xg5e_BCUGNLBEbC56#;vZyG z{eIVVhkq5wLw~K=*3Kv~8Lu*0-59c6iEjuO8USuBUF+|7AnrQ8J=%@p&6m^;XdM1j zCKBpLrkwV=*dzbt>6E3vH3$@GTHOM7%SSNGNp&1h?B!o`sgXl{N~7sVN9X&C4XJ+@ zAIDv%YCi??=X&jn^^KPyQj-|5xf>M(+_nah>9w$5?SE4Pfb+;Bx#ZrlUx2=#Z-%<(;)CMLNFT!2`Yw2hW8Bt z2^2979+OK69#NC9O71OfpC00bZa wCbm1Z%CM>dnd%M91rZD#kYecnLR_?Ouxg((x&KE52z1x*fYzzv{faKX z;9ZJqBE3Y(w<9wUe~rhq=80}hIhW2a;FN#+$qX-%&ZdhLeN?SW$bFMS5nerPSPX3R z9;Lcqtgmo%4yM;)mRl^46d+AnGdUdP*_SwcCHUm4D;P&BF7`2ceS+-0$&f$uOCUnM=-0@#I>|Ya*5d&)=nFY!8gQ zw&%^q`)J!&CzP(BTf)5KKjY}|+yrcg78(UoHw;+!Xq{2N5At!dpqaM$TpNfAD3sfO ztCR4uZ6|l#e^Ye~YW4JJpA*)2f}OOwNqHygQ*uV%y;zu?*Ravn<#E(CAD(>C;fdB$ zCPnKl7Xi@IrC~+$XW@cx{_hA`y>i=VE!AQ#L35_vu-JXsO~baX!MZzjxSzVWOTScB z7$DvaSA$wz7Koy zb*y#shCjHu&XUKf0I0oWX5jTN`!#0s|iAR>?)kr;wgm`DKqzVKEqeb zP8v8sln}w+WG^Zsyf5lcWS8f7H+0tWYq9cQtj?nsgVD#{_}|(5qzpvSU8$L)H8yuaMzW zdqV*{^_0nN&n-UyZaQw%XKW-n&1L@jUvqO|OAe301q1E?-fH7a=V+H?dX=vc>PQlB z8*o{23T-~<**~V9Yzhi$6Lfr)922z6aL>O;S9EIF77Xu4*tmoCe_RLy2A>*t3KiFU zMgzh&H_gxhLVN$xuUri->(cT~9#UTuOEetECtFxA=Paj`c@3o~E8<<+okESl3g8S& zs2>o>PJaod>xm8{HnD?T|0C)5aYx^tT0VoT5eJHqLLWEjLaIPeHV!~7d3@mg8&xx0 zw3O+-6_DCT%sLvsA9|$4sVP0*4Vuf2rd6pm^jij&{}5(_Mf)EgxigAb73^}(-B>+F*fR;r1r`c ziVYhi*LABC%4!wimINP1IUVJg%f)*pb;Gj9`XuTvf4^H+p5%MPz*nxOU9EPWRJ zLKR$~e3;ffr-AJ|pN;B1P+AY9MA)k*h#X>d(7C}N z3jhrO+jLvD>wKn!lrYL}!L_Higw>l2(+83M^m*1iIN{vXr1GFZ z!ME#YJQ0v(72e7jD$dS_^T%RzF8&k9mfbxHe-18JnbyyG`tV9wa2QC}G5yJ)W}Rci zFY&FI_ZrtmSnC0Vl)Lo|_-O+b!}i$5T$Rn&351+Wr;bhWsSCRzj>XuIiGI-bju^GL z)OxT+TDdPRg?#+=gmt;a(bFOEiiE>CtWZ#?yUm1fzIFG9viEom6rw{lWsfDr0PD`% ze<~|w9gA@4a<9y+32eoV9UnCF14)tTc6ON0!FCHCVW+kIRmrNBgW(69%cDJl<&EwI z!0?C)@sR@2gdoKM7r~zsz24p1vy^+r)s$kUpV|5HFYO)S+B&cVb!_qoes+G@IE1Dn zxldhXv6*|5D7meXwb$by{E<7-O+ZAYf9L726LcNi1=0(_6W@GvkmFGuP1(Vqth+zN z%m)69Dv{Z^O1JgaqcdWyD-yMBnAuLmUX(S7^+>*Xm?yUgmbyjF57P`md5u(+wv}Lg z9{FlEQeu2@H2CAtrfTnrpD;l%F)$4V31Egu0c8UO0s#d81R&{}fBi&Z{TJ5k n-e8X0kk;RA_%Lqd8&`Gs?n=u$gpdRX9u5A##Zs140s;sCXa#fk diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 b/iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 index 3605f00030abd4b17c5c7a6b83e47b76cb2a473d..f672208e9d76f50609243636d28972812886d5eb 100644 GIT binary patch delta 961 zcmV;y13vt_2)hW7YY0pRrk_f`4)TiFyT~`Kj1HhI>IkMJ=&X#zgQxG*J+d zm=BUTXoC)HIK_?E?&(-#l^MjiWIJN z{vLgKJhl2<@Q{ovjjx3-qA$L%l!4m{Y;#19Y5`ME+u!G z6ZTs|F=?08hN@26!^bP_?-umisvW}%!HvQW_9PAN{qf}*Mh#B!PSr`4b2?9nhSz67 zF@C<>KCv!Uo`}}WI)9BENne$TQf_yMJwe|Hwy2TRzJ&m9rmdJw*3-SZdy-?hj0HoS z8T+9$_xO6U!6zCRjNs=()gIWnwnWYec*(T-UQl2|7mWa?%L*aQ6Oz2)2SL`HukbN9 z=!=IfyPEO2nYIGCQk`+VFhMXeFbxI?V1`HmWdj5P0R;dAATB5cgFVFl! z_yl>V-nsx!e|KFg4=IQCx6_A zlt?>(7M^|?-`~xlpMW9ICpSqWN-3uU6WzmDpp8{C9 zbepCTkt)*+yIONYUKw7YxxnsIOr!CEd-0W6AcF5i8*JApd45DA@ece9IS36g9c#_2 zUOe`J;4_E4d^La$cUl{&wsf$5~p*DmG6Ep}CtEatr9Do2DYwgucWS=W=6-|ur%t-dic=MeeNBrq)m#&mb_+LM=1WjK`AwDwYS`Z=c=Zq34mDx5Qx<-8iPuC} z(A0`1;RIy=sV)WY=o6Xn%+72rBy|P^RcqQhy?@1)b7}(__Qf&)Vt?VcI}I9D;yn|- z)}&@b63<>!U_G#LoWU;kO`uxw8QEu2p2{2IC_>0x?c5@&&nR)hEno|RWitd`Li@Vp zqI0vOq%Q1Y2pDzQ6{#ph#H_7I=`^MF+3$JAyK=@UIH~K8E@)Hs7Zl=+m`4kVF7|^A zFKyAIM(SWo=60gMD1To8m|wJH^*T&caZD&UO;~pvztkLk{Hxu4K-V{#$0&och%8wx zufyYle(!^Uh7)sJm@#Ab!qD5CCq{sU)r@zsn02-Piaj0$eE$I^954}g6N|Ut9@%z= z8Ih{dEw#uYT;syap#xy&FhMXeFbxI?V1`HmWdj5P0R;dAATIXLR!n03z>-sXY<-Mo jNzjD!p__CyAXYbJdPgLc>0Sf~PT%%-UF>3y0s;sCt>DH5 diff --git a/iotdb-client/client-cpp/test/main.cpp b/iotdb-client/client-cpp/test/main.cpp index 1bc3425882fa..b77006ecd429 100644 --- a/iotdb-client/client-cpp/test/main.cpp +++ b/iotdb-client/client-cpp/test/main.cpp @@ -32,12 +32,8 @@ struct SessionListener : Catch::TestEventListenerBase { void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { if (!session) { SessionBuilder builder; - session = builder.host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->useSSL(false) - ->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + session = builder.build(); } else { session->open(false); } diff --git a/iotdb-client/client-cpp/test/main_Relational.cpp b/iotdb-client/client-cpp/test/main_Relational.cpp index de808c23224e..dcb045e25fe6 100644 --- a/iotdb-client/client-cpp/test/main_Relational.cpp +++ b/iotdb-client/client-cpp/test/main_Relational.cpp @@ -30,8 +30,8 @@ struct SessionListener : Catch::TestEventListenerBase { void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { if (!session) { TableSessionBuilder builder; - session = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + session = builder.build(); } else { session->open(); } diff --git a/iotdb-client/client-cpp/test/main_rpc_ntls.cpp b/iotdb-client/client-cpp/test/main_rpc_ntls.cpp new file mode 100644 index 000000000000..ec0bec9adb32 --- /dev/null +++ b/iotdb-client/client-cpp/test/main_rpc_ntls.cpp @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define CATCH_CONFIG_MAIN +#include diff --git a/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py new file mode 100644 index 000000000000..79c5a19de111 --- /dev/null +++ b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Patch IoTDB distribution RPC SSL settings for C++ integration tests.""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +import time +from pathlib import Path + +STORE_PASSWORD = "thrift" +SERVER_PKCS12 = "tls-server.p12" + + +def replace_property(text: str, key: str, value: str) -> str: + pattern = re.compile(rf"^{re.escape(key)}=.*$", re.MULTILINE) + replacement = f"{key}={value}" + if pattern.search(text): + return pattern.sub(replacement, text, count=1) + return text.rstrip() + "\n" + replacement + "\n" + + +def stop_iotdb(dist_root: Path) -> None: + if sys.platform == "win32": + stop_script = dist_root / "sbin" / "windows" / "stop-standalone.bat" + else: + stop_script = dist_root / "sbin" / "stop-standalone.sh" + if not stop_script.is_file(): + print(f"stop script not found, skip stop: {stop_script}", file=sys.stderr) + return + print(f"Stopping IoTDB via {stop_script}") + subprocess.run([str(stop_script)], cwd=str(dist_root), check=False, shell=True) + time.sleep(15) + + +def configure_plain(dist_root: Path) -> int: + props_path = dist_root / "conf" / "iotdb-system.properties" + if not props_path.is_file(): + print(f"iotdb-system.properties not found: {props_path}", file=sys.stderr) + return 1 + + text = props_path.read_text(encoding="utf-8") + text = replace_property(text, "enable_thrift_ssl", "false") + text = replace_property(text, "thrift_ssl_client_auth", "false") + text = replace_property(text, "key_store_path", "") + text = replace_property(text, "key_store_pwd", "") + text = replace_property(text, "trust_store_path", "") + text = replace_property(text, "trust_store_pwd", "") + text = replace_property(text, "ssl_protocol", "TLS") + props_path.write_text(text, encoding="utf-8", newline="\n") + print(f"Configured plain RPC in {props_path}") + return 0 + + +def configure_tls(dist_root: Path, fixtures_root: Path) -> int: + props_path = dist_root / "conf" / "iotdb-system.properties" + if not props_path.is_file(): + print(f"iotdb-system.properties not found: {props_path}", file=sys.stderr) + return 1 + + ssl_dir = dist_root / "conf" / "cpp-ssl-it" + ssl_dir.mkdir(parents=True, exist_ok=True) + source = fixtures_root / "tls" / SERVER_PKCS12 + if not source.is_file(): + print(f"fixture missing: {source}", file=sys.stderr) + return 1 + shutil.copy2(source, ssl_dir / SERVER_PKCS12) + + key_store = (ssl_dir / SERVER_PKCS12).as_posix() + + text = props_path.read_text(encoding="utf-8") + text = replace_property(text, "enable_thrift_ssl", "true") + text = replace_property(text, "thrift_ssl_client_auth", "false") + text = replace_property(text, "key_store_path", key_store) + text = replace_property(text, "key_store_pwd", STORE_PASSWORD) + text = replace_property(text, "trust_store_path", "") + text = replace_property(text, "trust_store_pwd", "") + text = replace_property(text, "ssl_protocol", "TLS") + props_path.write_text(text, encoding="utf-8", newline="\n") + print(f"Configured TLS IT server properties in {props_path}") + return 0 + + +def main() -> int: + if len(sys.argv) < 3: + print( + "usage: configure_iotdb_ssl_it.py [enable|disable]", + file=sys.stderr, + ) + return 2 + + dist_root = Path(sys.argv[1]).resolve() + fixtures_root = Path(sys.argv[2]).resolve() + mode = sys.argv[3].lower() if len(sys.argv) >= 4 else "enable" + + if mode in ("disable", "plain", "off"): + stop_iotdb(dist_root) + return configure_plain(dist_root) + + if mode in ("enable", "tls", "on"): + stop_iotdb(dist_root) + return configure_tls(dist_root, fixtures_root) + + print(f"unknown mode: {mode}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py new file mode 100644 index 000000000000..3aa7d9d3ce0c --- /dev/null +++ b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Run C++ client integration tests in two IoTDB modes: plain then TLS.""" + +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from pathlib import Path + + +def run(cmd: list[str], cwd: Path) -> None: + print(f"+ {' '.join(cmd)}", flush=True) + subprocess.run(cmd, cwd=str(cwd), check=True) + + +def stop_iotdb(dist_root: Path) -> None: + if sys.platform == "win32": + stop_script = dist_root / "sbin" / "windows" / "stop-standalone.bat" + else: + stop_script = dist_root / "sbin" / "stop-standalone.sh" + if not stop_script.is_file(): + print(f"stop script not found, skip stop: {stop_script}", file=sys.stderr) + return + print(f"Stopping IoTDB via {stop_script}") + subprocess.run([str(stop_script)], cwd=str(dist_root), check=False, shell=True) + time.sleep(15) + + +def start_iotdb(dist_root: Path, start_script: Path, wait_s: int) -> None: + if not start_script.is_file(): + raise FileNotFoundError(f"start script not found: {start_script}") + print(f"Starting IoTDB via {start_script}") + subprocess.Popen( + [str(start_script)], + cwd=str(dist_root), + shell=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f"Waiting {wait_s}s for IoTDB to become ready") + time.sleep(wait_s) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("build_dir", help="CMake build directory containing CTestTestfile.cmake") + parser.add_argument("dist_root", help="IoTDB distribution root") + parser.add_argument("fixtures_root", help="C++ test fixtures root") + parser.add_argument("scripts_root", help="Directory containing configure_iotdb_ssl_it.py") + parser.add_argument("start_script", help="Relative path to start-standalone script under dist sbin/") + parser.add_argument("--config", default="Release", help="CTest build configuration (MSVC)") + parser.add_argument("--wait-seconds", type=int, default=45, help="Seconds to wait after IoTDB start") + args = parser.parse_args() + + build_dir = Path(args.build_dir).resolve() + dist_root = Path(args.dist_root).resolve() + fixtures_root = Path(args.fixtures_root).resolve() + scripts_root = Path(args.scripts_root).resolve() + start_script = dist_root / "sbin" / args.start_script + + ctest_base = ["ctest", "-j", "1", "--output-on-failure"] + if args.config: + ctest_base.extend(["-C", args.config]) + + print("=== Phase 1: plain IoTDB (session IT + examples) ===") + run(ctest_base + ["-L", "plain"], build_dir) + + print("=== Phase 2: restart IoTDB with TLS (rpc SSL/NTLS IT) ===") + stop_iotdb(dist_root) + configure = scripts_root / "configure_iotdb_ssl_it.py" + run( + [sys.executable, str(configure), str(dist_root), str(fixtures_root), "enable"], + cwd=scripts_root, + ) + start_iotdb(dist_root, start_script, args.wait_seconds) + run(ctest_base + ["-L", "ssl"], build_dir) + print("=== Phase 2b: NTLS (no IoTDB; openssl s_server) ===") + run(ctest_base + ["-L", "ntls"], build_dir) + + print("All C++ integration test phases passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 29d90b868c30182610ca43ddd5c6609b2b4d2a2e Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 15:23:33 +0800 Subject: [PATCH 09/24] Set IOTDB_OPENSSL_ROOT_DIR for NTLS examples on Linux. Tongsuo openssl s_server needs the bundled libssl on LD_LIBRARY_PATH; rpc tests already pass the install root but NTLS examples only set the executable path. --- iotdb-client/client-cpp/examples/CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index fd0c4ecd41e3..fb5b2b3a4ae9 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -257,17 +257,21 @@ if(_iotdb_examples_in_tree AND WITH_SSL AND IOTDB_EXAMPLES_REGISTER_TESTS) if(BOOST_INCLUDE_DIR) target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") endif() + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir_cmake}") if(WIN32) file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl.exe" _iotdb_openssl_executable_cmake) string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") target_compile_definitions(${_t} PRIVATE - IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") else() file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl" _iotdb_openssl_executable_cmake) string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") target_compile_definitions(${_t} PRIVATE - IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") target_link_libraries(${_t} PRIVATE iotdb_thrift_static) endif() endforeach() From f3073252e9b2c7e2ddfcc5eaa03f0bbb3c1b14f3 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 15:45:57 +0800 Subject: [PATCH 10/24] Fix Windows Tongsuo build by using Strawberry Perl for Configure. Git Bash MSYS perl lacks Locale::Maketext::Simple required by OpenSSL Configure. Prefer C:/Strawberry/perl/bin/perl.exe and prepend it on CI bash steps. --- .github/workflows/client-cpp-package.yml | 6 ++++++ .github/workflows/multi-language-client.yml | 3 +++ iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake | 11 ++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/client-cpp-package.yml b/.github/workflows/client-cpp-package.yml index fe5fdc5630d5..9ad3fbb4a5ae 100644 --- a/.github/workflows/client-cpp-package.yml +++ b/.github/workflows/client-cpp-package.yml @@ -447,6 +447,12 @@ jobs: PACKAGE_CLASSIFIER: ${{ matrix.package_classifier }} run: | set -euxo pipefail + # Git Bash perl lacks modules for Tongsuo Configure; Strawberry is installed above. + if [ -d /c/Strawberry/perl/bin ]; then + export PATH="/c/Strawberry/perl/bin:${PATH}" + fi + which perl + perl -MLocale::Maketext::Simple -e1 MVN_ARGS=(./mvnw clean package -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests \ -Dspotless.skip=true \ "-Dclient.cpp.package.classifier=${PACKAGE_CLASSIFIER}") diff --git a/.github/workflows/multi-language-client.yml b/.github/workflows/multi-language-client.yml index bfcc8326b6f4..b86cdc307604 100644 --- a/.github/workflows/multi-language-client.yml +++ b/.github/workflows/multi-language-client.yml @@ -187,6 +187,9 @@ jobs: # Explicitly using "install" instead of package in order to be sure we're using libs built on this machine # (was causing problems on windows, but could cause problem on linux, when updating the thrift module) run: | + if [ "${{ runner.os }}" = "Windows" ] && [ -d /c/Strawberry/perl/bin ]; then + export PATH="/c/Strawberry/perl/bin:${PATH}" + fi if [[ "${{ matrix.os }}" == "windows-2025-vs2026" ]]; then ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am \ -Dcmake.generator="Visual Studio 18 2026" diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index f044ba75beda..c9f84ca65348 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -77,7 +77,16 @@ if(NOT EXISTS "${_tongsuo_stamp}") endif() if(WIN32) - find_program(PERL_EXECUTABLE perl REQUIRED) + # Git Bash ships a minimal MSYS perl that lacks modules required by + # Tongsuo/OpenSSL Configure (e.g. Locale::Maketext::Simple). Prefer + # Strawberry Perl installed by CI (choco) or local dev machines. + set(_strawberry_perl "C:/Strawberry/perl/bin/perl.exe") + if(EXISTS "${_strawberry_perl}") + set(PERL_EXECUTABLE "${_strawberry_perl}") + else() + find_program(PERL_EXECUTABLE NAMES perl.exe perl REQUIRED) + endif() + message(STATUS "[Tongsuo] using Perl: ${PERL_EXECUTABLE}") set(_tongsuo_target "VC-WIN64A") message(STATUS "[Tongsuo] configuring (${_tongsuo_target}) -> ${_tongsuo_inst}") execute_process( From f97642a5f0c564efc1266af8fdc685664da5b84b Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 3 Jul 2026 16:02:20 +0800 Subject: [PATCH 11/24] Run Tongsuo nmake under vcvars64 on Windows. Locate nmake next to cl.exe and invoke it via helper batch files so MSVC tools are on PATH during the OpenSSL/Tongsuo source build. --- .../client-cpp/cmake/FetchOpenSSL.cmake | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index c9f84ca65348..ba9f03949c80 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -87,6 +87,41 @@ if(NOT EXISTS "${_tongsuo_stamp}") find_program(PERL_EXECUTABLE NAMES perl.exe perl REQUIRED) endif() message(STATUS "[Tongsuo] using Perl: ${PERL_EXECUTABLE}") + find_program(NMAKE_EXECUTABLE nmake) + if(NOT NMAKE_EXECUTABLE AND CMAKE_CXX_COMPILER) + get_filename_component(_msvc_bin_dir "${CMAKE_CXX_COMPILER}" DIRECTORY) + find_program(NMAKE_EXECUTABLE nmake PATHS "${_msvc_bin_dir}" NO_DEFAULT_PATH) + endif() + if(NOT NMAKE_EXECUTABLE) + message(FATAL_ERROR "[Tongsuo] nmake not found (install VS Build Tools or run from Developer Command Prompt)") + endif() + message(STATUS "[Tongsuo] using nmake: ${NMAKE_EXECUTABLE}") + get_filename_component(_cl_exe "${CMAKE_CXX_COMPILER}" REALPATH) + set(_vc_dir "${_cl_exe}") + foreach(_unused RANGE 6) + get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) + endforeach() + set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + if(NOT EXISTS "${_vcvars}") + message(FATAL_ERROR "[Tongsuo] vcvars64.bat not found near ${CMAKE_CXX_COMPILER}") + endif() + file(TO_NATIVE_PATH "${_vcvars}" _vcvars_native) + file(TO_NATIVE_PATH "${NMAKE_EXECUTABLE}" _nmake_native) + file(TO_NATIVE_PATH "${_tongsuo_src}" _tongsuo_src_native) + set(_nmake_build_bat "${_tongsuo_root}/tongsuo-nmake-build.bat") + set(_nmake_install_bat "${_tongsuo_root}/tongsuo-nmake-install.bat") + file(WRITE "${_nmake_build_bat}" "@echo off\r\n") + file(APPEND "${_nmake_build_bat}" "call \"${_vcvars_native}\" amd64\r\n") + file(APPEND "${_nmake_build_bat}" "if errorlevel 1 exit /b 1\r\n") + file(APPEND "${_nmake_build_bat}" "cd /d \"${_tongsuo_src_native}\"\r\n") + file(APPEND "${_nmake_build_bat}" "\"${_nmake_native}\"\r\n") + file(APPEND "${_nmake_build_bat}" "exit /b %ERRORLEVEL%\r\n") + file(WRITE "${_nmake_install_bat}" "@echo off\r\n") + file(APPEND "${_nmake_install_bat}" "call \"${_vcvars_native}\" amd64\r\n") + file(APPEND "${_nmake_install_bat}" "if errorlevel 1 exit /b 1\r\n") + file(APPEND "${_nmake_install_bat}" "cd /d \"${_tongsuo_src_native}\"\r\n") + file(APPEND "${_nmake_install_bat}" "\"${_nmake_native}\" install_sw\r\n") + file(APPEND "${_nmake_install_bat}" "exit /b %ERRORLEVEL%\r\n") set(_tongsuo_target "VC-WIN64A") message(STATUS "[Tongsuo] configuring (${_tongsuo_target}) -> ${_tongsuo_inst}") execute_process( @@ -101,16 +136,14 @@ if(NOT EXISTS "${_tongsuo_stamp}") message(STATUS "[Tongsuo] building") execute_process( - COMMAND nmake - WORKING_DIRECTORY "${_tongsuo_src}" + COMMAND "${_nmake_build_bat}" RESULT_VARIABLE _rc) if(NOT _rc EQUAL 0) message(FATAL_ERROR "[Tongsuo] nmake failed (rc=${_rc})") endif() execute_process( - COMMAND nmake install_sw - WORKING_DIRECTORY "${_tongsuo_src}" + COMMAND "${_nmake_install_bat}" RESULT_VARIABLE _rc) if(NOT _rc EQUAL 0) message(FATAL_ERROR "[Tongsuo] nmake install_sw failed (rc=${_rc})") From 21d6125905e0417cb8fdb699dab148b38554632b Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Mon, 6 Jul 2026 09:32:37 +0800 Subject: [PATCH 12/24] Fix packaged SDK examples and Windows Tongsuo tool discovery. Ship only legacy examples in the release zip; IT-only TLS/NTLS smoke examples stay in-tree. Locate nmake/vcvars64 when CMAKE_CXX_COMPILER is unset (VS2017 matrix on GHA). --- .../client-cpp/cmake/FetchOpenSSL.cmake | 42 +++++++++++--- .../client-cpp/examples/CMakeLists.txt | 57 +++++++++++-------- .../client-cpp/src/assembly/client-cpp.xml | 8 ++- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index ba9f03949c80..af691b164d96 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -92,18 +92,46 @@ if(NOT EXISTS "${_tongsuo_stamp}") get_filename_component(_msvc_bin_dir "${CMAKE_CXX_COMPILER}" DIRECTORY) find_program(NMAKE_EXECUTABLE nmake PATHS "${_msvc_bin_dir}" NO_DEFAULT_PATH) endif() + if(NOT NMAKE_EXECUTABLE AND DEFINED ENV{VCINSTALLDIR}) + file(GLOB _nmake_candidates "$ENV{VCINSTALLDIR}/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe") + if(_nmake_candidates) + list(GET _nmake_candidates 0 NMAKE_EXECUTABLE) + endif() + endif() + if(NOT NMAKE_EXECUTABLE) + file(GLOB _nmake_candidates + "C:/Program Files (x86)/Microsoft Visual Studio/2017/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe" + "C:/Program Files/Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe" + "C:/Program Files/Microsoft Visual Studio/18/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe") + if(_nmake_candidates) + list(SORT _nmake_candidates COMPARE NATURAL ORDER DESCENDING) + list(GET _nmake_candidates 0 NMAKE_EXECUTABLE) + endif() + endif() if(NOT NMAKE_EXECUTABLE) message(FATAL_ERROR "[Tongsuo] nmake not found (install VS Build Tools or run from Developer Command Prompt)") endif() message(STATUS "[Tongsuo] using nmake: ${NMAKE_EXECUTABLE}") - get_filename_component(_cl_exe "${CMAKE_CXX_COMPILER}" REALPATH) - set(_vc_dir "${_cl_exe}") - foreach(_unused RANGE 6) - get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) - endforeach() - set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + set(_vcvars "") + if(CMAKE_CXX_COMPILER) + get_filename_component(_cl_exe "${CMAKE_CXX_COMPILER}" REALPATH) + set(_vc_dir "${_cl_exe}") + foreach(_unused RANGE 6) + get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) + endforeach() + set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + elseif(DEFINED ENV{VCINSTALLDIR}) + set(_vcvars "$ENV{VCINSTALLDIR}/Auxiliary/Build/vcvars64.bat") + else() + get_filename_component(_nmake_dir "${NMAKE_EXECUTABLE}" DIRECTORY) + set(_vc_dir "${_nmake_dir}") + foreach(_unused RANGE 6) + get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) + endforeach() + set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + endif() if(NOT EXISTS "${_vcvars}") - message(FATAL_ERROR "[Tongsuo] vcvars64.bat not found near ${CMAKE_CXX_COMPILER}") + message(FATAL_ERROR "[Tongsuo] vcvars64.bat not found (CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})") endif() file(TO_NATIVE_PATH "${_vcvars}" _vcvars_native) file(TO_NATIVE_PATH "${NMAKE_EXECUTABLE}" _nmake_native) diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index fb5b2b3a4ae9..8ef4f34be58d 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -141,42 +141,51 @@ ADD_EXECUTABLE(SessionExample SessionExample.cpp) ADD_EXECUTABLE(AlignedTimeseriesSessionExample AlignedTimeseriesSessionExample.cpp) ADD_EXECUTABLE(TableModelSessionExample TableModelSessionExample.cpp) ADD_EXECUTABLE(MultiSvrNodeClient MultiSvrNodeClient.cpp) -ADD_EXECUTABLE(cpp_tree_example cpp_tree_example.cpp) -ADD_EXECUTABLE(cpp_table_example cpp_table_example.cpp) -ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) -ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) ADD_EXECUTABLE(tree_example tree_example.c) ADD_EXECUTABLE(table_example table_example.c) -ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) -ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) set(_example_targets SessionExample AlignedTimeseriesSessionExample TableModelSessionExample MultiSvrNodeClient - cpp_tree_example - cpp_table_example - cpp_tls_example - cpp_ntls_example - tree_example - table_example - tls_tree_example - c_ntls_example) - -set(_it_plain_examples - cpp_tree_example - cpp_table_example tree_example table_example) -set(_it_ssl_examples - cpp_tls_example - tls_tree_example) +set(_it_plain_examples "") +set(_it_ssl_examples "") +set(_it_ntls_examples "") + +if(_iotdb_examples_in_tree) + ADD_EXECUTABLE(cpp_tree_example cpp_tree_example.cpp) + ADD_EXECUTABLE(cpp_table_example cpp_table_example.cpp) + ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) + ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) + ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) + ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) + + list(APPEND _example_targets + cpp_tree_example + cpp_table_example + cpp_tls_example + cpp_ntls_example + tls_tree_example + c_ntls_example) -set(_it_ntls_examples - cpp_ntls_example - c_ntls_example) + set(_it_plain_examples + cpp_tree_example + cpp_table_example + tree_example + table_example) + + set(_it_ssl_examples + cpp_tls_example + tls_tree_example) + + set(_it_ntls_examples + cpp_ntls_example + c_ntls_example) +endif() foreach(_t IN LISTS _example_targets) if(WITH_SSL AND _iotdb_ssl_link_libs) diff --git a/iotdb-client/client-cpp/src/assembly/client-cpp.xml b/iotdb-client/client-cpp/src/assembly/client-cpp.xml index 3a6a63136410..a9340b7f95ff 100644 --- a/iotdb-client/client-cpp/src/assembly/client-cpp.xml +++ b/iotdb-client/client-cpp/src/assembly/client-cpp.xml @@ -97,8 +97,12 @@ ${project.basedir}/examples CMakeLists.txt - *.c - *.cpp + SessionExample.cpp + AlignedTimeseriesSessionExample.cpp + TableModelSessionExample.cpp + MultiSvrNodeClient.cpp + tree_example.c + table_example.c examples From 9fe80d873229a476dec72b51d9b3dd72ba0467a2 Mon Sep 17 00:00:00 2001 From: hongzhigao <761417898@qq.com> Date: Tue, 7 Jul 2026 23:46:39 +0800 Subject: [PATCH 13/24] C++ client: add TLS/TLCP SSL support with bundled Tongsuo Build libssl/libcrypto from Tongsuo 8.4-stable (ASF-compliant) instead of system OpenSSL, pin Thrift to 6dfb0b26, and wire SSL through RpcSslUtils with PKCS12 trust/keystore validation. Add plain/TLS/NTLS integration tests, example programs, multi-platform CI packaging, and macOS header-wrap fixes so Homebrew OpenSSL does not shadow Tongsuo NTLS APIs on CI runners. --- .../package-client-cpp-manylinux228.sh | 9 +- .github/workflows/client-cpp-package.yml | 42 +- .github/workflows/multi-language-client.yml | 35 +- iotdb-client/client-cpp/CMakeLists.txt | 29 +- iotdb-client/client-cpp/README.md | 149 +++- iotdb-client/client-cpp/README_zh.md | 74 +- .../client-cpp/cmake/FetchBuildTools.cmake | 2 +- .../client-cpp/cmake/FetchOpenSSL.cmake | 322 +++++--- .../client-cpp/cmake/FetchThrift.cmake | 30 +- .../client-cpp/cmake/PatchThriftSsl.cmake | 80 ++ .../cmake/TongsuoOpenSslHeaders.cmake | 47 ++ .../AlignedTimeseriesSessionExample.cpp | 1 + .../client-cpp/examples/CMakeLists.txt | 225 ++++-- .../examples/ExampleNtlsHandshake.cpp | 80 ++ .../examples/ExampleNtlsHandshake.h | 34 + .../client-cpp/examples/ExampleTlsConfig.cpp | 136 ++++ .../client-cpp/examples/ExampleTlsConfig.h | 53 ++ iotdb-client/client-cpp/examples/README.md | 67 +- iotdb-client/client-cpp/examples/README_zh.md | 44 +- .../client-cpp/examples/SessionExample.cpp | 1 + .../examples/TableModelSessionExample.cpp | 25 +- .../client-cpp/examples/c_ntls_example.c | 31 + .../client-cpp/examples/cpp_ntls_example.cpp | 31 + .../client-cpp/examples/cpp_table_example.cpp | 57 ++ .../client-cpp/examples/cpp_tls_example.cpp | 63 ++ .../client-cpp/examples/cpp_tree_example.cpp | 66 ++ .../client-cpp/examples/tls_tree_example.c | 86 +++ iotdb-client/client-cpp/pom.xml | 57 +- .../client-cpp/src/assembly/client-cpp.xml | 18 +- .../third_party/DEPENDENCIES.md | 4 +- .../package-metadata/third_party/NOTICE | 6 +- .../src/include/AbstractSessionBuilder.h | 6 + iotdb-client/client-cpp/src/include/Session.h | 3 + .../client-cpp/src/include/SessionBuilder.h | 25 + .../client-cpp/src/include/SessionC.h | 18 + .../client-cpp/src/include/SessionPool.h | 37 +- .../client-cpp/src/include/TableSession.h | 3 + .../src/include/TableSessionBuilder.h | 25 + .../client-cpp/src/rpc/NodesSupplier.cpp | 31 +- .../client-cpp/src/rpc/NodesSupplier.h | 11 +- .../client-cpp/src/rpc/RpcSslUtils.cpp | 695 +++++++++++++++++ iotdb-client/client-cpp/src/rpc/RpcSslUtils.h | 77 ++ .../client-cpp/src/rpc/SessionConnection.cpp | 13 +- .../client-cpp/src/rpc/SessionConnection.h | 2 +- iotdb-client/client-cpp/src/rpc/SessionImpl.h | 4 +- .../client-cpp/src/rpc/ThriftConnection.cpp | 11 +- .../client-cpp/src/rpc/ThriftConnection.h | 6 +- .../client-cpp/src/session/Session.cpp | 20 +- .../client-cpp/src/session/SessionC.cpp | 195 ++++- .../client-cpp/src/session/SessionPool.cpp | 30 + .../client-cpp/src/session/TableSession.cpp | 5 + iotdb-client/client-cpp/test/CMakeLists.txt | 112 ++- .../client-cpp/test/cpp/ItSslConnection.cpp | 180 +++++ .../client-cpp/test/cpp/ItSslConnection.h | 59 ++ .../client-cpp/test/cpp/RpcNtlsE2eTest.cpp | 113 +++ .../test/cpp/RpcSslIotdbE2eTest.cpp | 175 +++++ .../test/cpp/RpcSslTlcpMutualAuthTest.cpp | 60 ++ .../test/cpp/RpcSslTlsMutualAuthTest.cpp | 167 +++++ .../client-cpp/test/cpp/RpcSslUtilsTest.cpp | 67 ++ .../client-cpp/test/cpp/SslTestFixtures.cpp | 705 ++++++++++++++++++ .../client-cpp/test/cpp/SslTestFixtures.h | 80 ++ .../test/cpp/sessionCRelationalIT.cpp | 7 +- .../client-cpp/test/cpp/sessionIT.cpp | 58 +- .../test/cpp/sessionRelationalIT.cpp | 8 +- .../client-cpp/test/fixtures/.gitignore | 22 + .../client-cpp/test/fixtures/README.md | 23 + .../test/fixtures/generate_fixtures.cmd | 69 ++ .../client-cpp/test/fixtures/tlcp/ca.crt | 11 + .../test/fixtures/tlcp/client_enc.crt | 9 + .../test/fixtures/tlcp/client_enc.key | 8 + .../test/fixtures/tlcp/client_sign.crt | 9 + .../test/fixtures/tlcp/client_sign.key | 8 + .../test/fixtures/tlcp/server_enc.crt | 9 + .../test/fixtures/tlcp/server_enc.key | 8 + .../test/fixtures/tlcp/server_sign.crt | 9 + .../test/fixtures/tlcp/server_sign.key | 8 + .../test/fixtures/tlcp/tlcp-client-enc.p12 | Bin 0 -> 1029 bytes .../test/fixtures/tlcp/tlcp-client-sign.p12 | Bin 0 -> 1031 bytes .../test/fixtures/tlcp/tlcp-trust.p12 | Bin 0 -> 683 bytes .../client-cpp/test/fixtures/tls/ca.crt | 19 + .../client-cpp/test/fixtures/tls/client.crt | 17 + .../client-cpp/test/fixtures/tls/client.key | 28 + .../client-cpp/test/fixtures/tls/server.crt | 19 + .../client-cpp/test/fixtures/tls/server.key | 28 + .../test/fixtures/tls/tls-client.p12 | Bin 0 -> 2512 bytes .../test/fixtures/tls/tls-server.p12 | Bin 0 -> 2608 bytes .../test/fixtures/tls/tls-trust.p12 | Bin 0 -> 1083 bytes iotdb-client/client-cpp/test/main.cpp | 8 +- .../client-cpp/test/main_Relational.cpp | 4 +- .../client-cpp/test/main_rpc_ntls.cpp | 21 + iotdb-client/client-cpp/test/main_rpc_ssl.cpp | 21 + .../test/scripts/configure_iotdb_ssl_it.py | 128 ++++ .../test/scripts/run_cpp_it_phases.py | 104 +++ .../client-cpp/test/tools/GenTlcpDualP12.cpp | 159 ++++ iotdb-client/client-cpp/third-party/README.md | 6 +- pom.xml | 3 + 96 files changed, 5278 insertions(+), 392 deletions(-) create mode 100644 iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake create mode 100644 iotdb-client/client-cpp/cmake/TongsuoOpenSslHeaders.cmake create mode 100644 iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp create mode 100644 iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h create mode 100644 iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp create mode 100644 iotdb-client/client-cpp/examples/ExampleTlsConfig.h create mode 100644 iotdb-client/client-cpp/examples/c_ntls_example.c create mode 100644 iotdb-client/client-cpp/examples/cpp_ntls_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_table_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_tls_example.cpp create mode 100644 iotdb-client/client-cpp/examples/cpp_tree_example.cpp create mode 100644 iotdb-client/client-cpp/examples/tls_tree_example.c create mode 100644 iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp create mode 100644 iotdb-client/client-cpp/src/rpc/RpcSslUtils.h create mode 100644 iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/ItSslConnection.h create mode 100644 iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp create mode 100644 iotdb-client/client-cpp/test/cpp/SslTestFixtures.h create mode 100644 iotdb-client/client-cpp/test/fixtures/.gitignore create mode 100644 iotdb-client/client-cpp/test/fixtures/README.md create mode 100644 iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/client.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/client.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/server.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/server.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-client.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 create mode 100644 iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 create mode 100644 iotdb-client/client-cpp/test/main_rpc_ntls.cpp create mode 100644 iotdb-client/client-cpp/test/main_rpc_ssl.cpp create mode 100644 iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py create mode 100644 iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py create mode 100644 iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp diff --git a/.github/scripts/package-client-cpp-manylinux228.sh b/.github/scripts/package-client-cpp-manylinux228.sh index 6bfef0aa415b..a90aaaa75e38 100644 --- a/.github/scripts/package-client-cpp-manylinux228.sh +++ b/.github/scripts/package-client-cpp-manylinux228.sh @@ -73,10 +73,10 @@ java -version # manylinux_2_28 is AlmaLinux 8, whose system OpenSSL is 1.1.1 (EOL and not # Apache-2.0 - must not be bundled/redistributed in an ASF convenience binary). -# Build OpenSSL 3.x from source instead (-Diotdb.openssl.from.source=ON), which -# keeps the glibc 2.28 baseline. OpenSSL 3.x's Configure needs perl plus a few -# modules (IPC::Cmd, Data::Dumper) that are not on the minimal image - install -# them even when perl itself is already present. +# Tongsuo 8.4-stable is always built from source (WITH_SSL=ON), which keeps the +# glibc 2.28 baseline. Tongsuo's Configure needs perl plus a +# few modules (IPC::Cmd, Data::Dumper) that are not on the minimal image - +# install them even when perl itself is already present. if command -v dnf >/dev/null 2>&1; then dnf install -y perl perl-IPC-Cmd perl-Data-Dumper else @@ -86,7 +86,6 @@ fi cd "${GITHUB_WORKSPACE:?GITHUB_WORKSPACE is not set}" ./mvnw clean package -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests \ -Dspotless.skip=true \ - -Diotdb.openssl.from.source=ON \ -Dclient.cpp.package.classifier="${PACKAGE_CLASSIFIER}" SO="iotdb-client/client-cpp/target/install/lib/libiotdb_session.so" diff --git a/.github/workflows/client-cpp-package.yml b/.github/workflows/client-cpp-package.yml index 38eac3fbcbcc..4ccdc2956059 100644 --- a/.github/workflows/client-cpp-package.yml +++ b/.github/workflows/client-cpp-package.yml @@ -64,7 +64,7 @@ jobs: cpp=false while IFS= read -r file; do case "$file" in - iotdb-client/client-cpp/*|iotdb-client/pom.xml|iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift|iotdb-protocol/thrift-commons/src/main/thrift/common.thrift|.github/workflows/client-cpp-package.yml|.github/scripts/package-client-cpp-*.sh) + pom.xml|iotdb-client/client-cpp/*|iotdb-client/pom.xml|iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift|iotdb-protocol/thrift-commons/src/main/thrift/common.thrift|.github/workflows/client-cpp-package.yml|.github/scripts/package-client-cpp-*.sh) cpp=true break ;; @@ -309,16 +309,16 @@ jobs: shell: bash run: | set -euxo pipefail - # Pin openssl@3 (Apache-2.0): the default 'openssl' formula will move to - # OpenSSL 4.0, which drops the legacy TLS-method APIs Thrift still uses. - brew install boost openssl@3 llvm@17 bison + # Build Tongsuo from source for SSL/TLS (国密 / TLCP support). + brew install boost llvm@17 bison perl ln -sf "$(brew --prefix llvm@17)/bin/clang-format" "$(brew --prefix)/bin/clang-format" echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" echo "$(brew --prefix llvm@17)/bin" >> "$GITHUB_PATH" - # Homebrew OpenSSL is keg-only, so point find_package(OpenSSL) at it. - echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + echo "$(brew --prefix perl)/bin" >> "$GITHUB_PATH" clang-format --version bison --version + perl --version + perl -MLocale::Maketext::Simple -e1 - name: Cache Maven packages uses: actions/cache@v5 with: @@ -332,6 +332,8 @@ jobs: MACOSX_DEPLOYMENT_TARGET: "12.0" run: | set -euxo pipefail + # Homebrew injects OpenSSL headers via CPATH ahead of bundled Tongsuo. + unset CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH || true ./mvnw clean package -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests \ -Dspotless.skip=true - name: Resolve package zip @@ -420,6 +422,7 @@ jobs: shell: pwsh run: | choco install winflexbison3 -y --no-progress + choco install strawberryperl -y --no-progress $boostArgs = @('install', '${{ matrix.boost_choco }}', '-y', '--no-progress') if ('${{ matrix.boost_choco_version }}' -ne '') { $boostArgs += @("--version=${{ matrix.boost_choco_version }}") @@ -433,18 +436,7 @@ jobs: throw "Boost not found under C:\local after installing ${{ matrix.boost_choco }}" } echo $boostDir.FullName >> $env:GITHUB_PATH - # Use a pinned OpenSSL 3.x (Apache-2.0). 'choco install openssl' now - # installs OpenSSL 4.0, which removed the legacy TLS-method APIs that - # Apache Thrift's TSSLSocket still calls. The FireDaemon zip is a clean - # prebuilt OpenSSL 3.5.x that keeps them. - $sslZip = "$env:RUNNER_TEMP\openssl-3.5.3.zip" - $sslDir = "$env:RUNNER_TEMP\openssl-3" - curl.exe -L --fail --retry 3 -o $sslZip 'https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-3.5.3.zip' - Expand-Archive -Path $sslZip -DestinationPath $sslDir -Force - $sslPath = (Get-ChildItem $sslDir -Recurse -Directory -Filter 'x64' | Select-Object -First 1).FullName - if (-not $sslPath) { throw "OpenSSL x64 dir not found under $sslDir" } - echo "$sslPath\bin" >> $env:GITHUB_PATH - echo "OPENSSL_ROOT_DIR=$sslPath" >> $env:GITHUB_ENV + echo "C:\strawberry\perl\bin" >> $env:GITHUB_PATH - name: Cache Maven packages uses: actions/cache@v5 with: @@ -459,6 +451,12 @@ jobs: PACKAGE_CLASSIFIER: ${{ matrix.package_classifier }} run: | set -euxo pipefail + # Git Bash perl lacks modules for Tongsuo Configure; Strawberry is installed above. + if [ -d /c/Strawberry/perl/bin ]; then + export PATH="/c/Strawberry/perl/bin:${PATH}" + fi + which perl + perl -MLocale::Maketext::Simple -e1 MVN_ARGS=(./mvnw clean package -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests \ -Dspotless.skip=true \ "-Dclient.cpp.package.classifier=${PACKAGE_CLASSIFIER}") @@ -529,8 +527,12 @@ jobs: test -n "${PKG_ROOT}" EXAMPLE_BUILD="${TEMP_BASE}/client-cpp-example-smoke" CMAKE_ARGS=(-S "${PKG_ROOT}/examples" -B "${EXAMPLE_BUILD}" -DIOTDB_SDK_ROOT="${PKG_ROOT}") - if [ -n "${CMAKE_GENERATOR:-}" ]; then - CMAKE_ARGS+=(-G "${CMAKE_GENERATOR}") + GENERATOR="${CMAKE_GENERATOR:-}" + if [ "${RUNNER_OS}" = "Windows" ] && [ -z "${GENERATOR}" ]; then + GENERATOR="Visual Studio 17 2022" + fi + if [ -n "${GENERATOR}" ]; then + CMAKE_ARGS+=(-G "${GENERATOR}") # windows-x86_64 SDK; VS2017/2019 default to Win32 without -A x64 (see client-cpp pom). CMAKE_ARGS+=(-A x64) fi diff --git a/.github/workflows/multi-language-client.yml b/.github/workflows/multi-language-client.yml index 5437a6549856..ca209cd3e637 100644 --- a/.github/workflows/multi-language-client.yml +++ b/.github/workflows/multi-language-client.yml @@ -6,6 +6,7 @@ on: - master - "rc/*" paths: + - 'pom.xml' - 'iotdb-client/pom.xml' - 'iotdb-client/client-py/**' - 'iotdb-client/client-cpp/**' @@ -20,6 +21,7 @@ on: - "rc/*" - 'force_ci/**' paths: + - 'pom.xml' - 'iotdb-client/pom.xml' - 'iotdb-client/client-py/**' - 'iotdb-client/client-cpp/**' @@ -80,7 +82,7 @@ jobs: go=false while IFS= read -r file; do case "$file" in - iotdb-client/pom.xml|iotdb-client/client-cpp/*|iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift|iotdb-protocol/thrift-commons/src/main/thrift/common.thrift|.github/workflows/multi-language-client.yml|.github/workflows/client-cpp-package.yml|.github/scripts/package-client-cpp-*.sh) + pom.xml|iotdb-client/pom.xml|iotdb-client/client-cpp/*|iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift|iotdb-protocol/thrift-commons/src/main/thrift/common.thrift|.github/workflows/multi-language-client.yml|.github/workflows/client-cpp-package.yml|.github/scripts/package-client-cpp-*.sh) cpp=true ;; esac @@ -124,7 +126,7 @@ jobs: run: | set -euxo pipefail sudo apt-get update - sudo apt-get install -y libboost-all-dev openssl libssl-dev wget + sudo apt-get install -y libboost-all-dev perl wget # jammy (22.04): no clang-format-17 in default repos — use apt.llvm.org (same LLVM 17 as noble/choco/brew) . /etc/os-release if [[ "${VERSION_CODENAME}" == "jammy" ]]; then @@ -144,13 +146,12 @@ jobs: if: runner.os == 'macOS' shell: bash run: | - # Pin openssl@3 (Apache-2.0); the default formula will move to OpenSSL 4.0. - brew install boost openssl@3 llvm@17 bison + # Build Tongsuo from source for SSL/TLS (国密 / TLCP support). + brew install boost llvm@17 bison perl ln -sf "$(brew --prefix llvm@17)/bin/clang-format" "$(brew --prefix)/bin/clang-format" echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" echo "$(brew --prefix llvm@17)/bin" >> "$GITHUB_PATH" - # Homebrew OpenSSL is keg-only, so point find_package(OpenSSL) at it. - echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + echo "$(brew --prefix perl)/bin" >> "$GITHUB_PATH" clang-format --version bison --version sudo rm -rf /Applications/Xcode_14.3.1.app @@ -163,19 +164,10 @@ jobs: run: | choco install winflexbison3 -y choco install boost-msvc-14.3 -y + choco install strawberryperl -y $boost_path = (Get-ChildItem -Path 'C:\local\' -Filter 'boost_*').FullName echo $boost_path >> $env:GITHUB_PATH - - # Pinned OpenSSL 3.x (Apache-2.0): 'choco install openssl' now installs - # OpenSSL 4.0, which removed the legacy TLS-method APIs Thrift uses. - $sslZip = "$env:RUNNER_TEMP\openssl-3.5.3.zip" - $sslDir = "$env:RUNNER_TEMP\openssl-3" - curl.exe -L --fail --retry 3 -o $sslZip 'https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-3.5.3.zip' - Expand-Archive -Path $sslZip -DestinationPath $sslDir -Force - $sslPath = (Get-ChildItem $sslDir -Recurse -Directory -Filter 'x64' | Select-Object -First 1).FullName - if (-not $sslPath) { throw "OpenSSL x64 dir not found under $sslDir" } - echo "$sslPath\bin" >> $env:GITHUB_PATH - echo "OPENSSL_ROOT_DIR=$sslPath" >> $env:GITHUB_ENV + echo "C:\strawberry\perl\bin" >> $env:GITHUB_PATH choco install llvm --version=17.0.6 --force -y clang-format --version - name: Cache Maven packages @@ -197,8 +189,15 @@ jobs: # Explicitly using "install" instead of package in order to be sure we're using libs built on this machine # (was causing problems on windows, but could cause problem on linux, when updating the thrift module) run: | + if [ "${{ runner.os }}" = "Windows" ] && [ -d /c/Strawberry/perl/bin ]; then + export PATH="/c/Strawberry/perl/bin:${PATH}" + fi + if [ "${{ runner.os }}" = "macOS" ]; then + unset CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH || true + fi if [[ "${{ matrix.os }}" == "windows-2025-vs2026" ]]; then - ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am -Dcmake.generator="Visual Studio 18 2026" + ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am \ + -Dcmake.generator="Visual Studio 18 2026" else ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am fi diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index ad357dd61a9d..2581efc07d53 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -78,7 +78,7 @@ if(NOT MSVC) file(WRITE "${_iotdb_cxx11_abi_stamp}" "${_iotdb_cxx11_abi_stamp_value}") endif() -option(WITH_SSL "Build with OpenSSL support" ON) +option(WITH_SSL "Build with Tongsuo SSL/TLS support" ON) option(BUILD_TESTING "Build IT test executables" OFF) option(IOTDB_OFFLINE "Disable all network access during configure" OFF) set(IOTDB_SESSION_VERSION "0.0.0" @@ -97,8 +97,12 @@ else() endif() set(BOOST_VERSION "${_iotdb_default_boost_version}" CACHE STRING "Boost version used when downloading / unpacking (Thrift build only)") -set(THRIFT_VERSION "0.23.0" - CACHE STRING "Apache Thrift version used when downloading / building") +set(THRIFT_GIT_COMMIT "6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242" + CACHE STRING "Apache Thrift git commit used when downloading / building") +set(TONGSUO_GIT_REF "8.4.0" + CACHE STRING "Tongsuo git tag/commit used when building SSL/TLS from source") +set(TONGSUO_TARBALL_SHA256 "57c2741750a699bfbdaa1bbe44a5733e9c8fc65d086c210151cfbc2bbd6fc975" + CACHE STRING "Expected SHA256 of the Tongsuo source tarball") if(WIN32) set(IOTDB_OS_DEPS_DIR "${IOTDB_DEPS_DIR}/windows") @@ -145,8 +149,8 @@ if(UNIX AND NOT APPLE) SOVERSION "${IOTDB_SESSION_SOVERSION}") endif() -# When SSL is on we bundle the OpenSSL shared libraries next to libiotdb_session -# in the package lib/ directory. Give the library an $ORIGIN-relative runtime +# When SSL is on we bundle the Tongsuo/OpenSSL-compatible shared libraries next to +# libiotdb_session in the package lib/ directory. Give the library an $ORIGIN-relative runtime # search path so the loader finds them without LD_LIBRARY_PATH / install_name # tweaks, keeping the SDK self-contained. if(WITH_SSL) @@ -194,7 +198,15 @@ else() endif() if(WITH_SSL) - target_link_libraries(iotdb_session PUBLIC OpenSSL::SSL OpenSSL::Crypto) + target_link_libraries(iotdb_session PUBLIC + OpenSSL::SSL OpenSSL::Crypto) + if(TARGET iotdb_tongsuo_openssl_wrap) + target_link_libraries(iotdb_session PUBLIC iotdb_tongsuo_openssl_wrap) + # BEFORE on a linked INTERFACE target does not always win over Homebrew's + # default -isystem paths on macOS; apply the wrap directory directly. + target_include_directories(iotdb_session BEFORE PUBLIC + "${IOTDB_TONGSUO_OPENSSL_WRAP_DIR}") + endif() target_compile_definitions(iotdb_session PUBLIC WITH_SSL=1) else() target_compile_definitions(iotdb_session PUBLIC WITH_SSL=0) @@ -240,8 +252,8 @@ install(TARGETS iotdb_session LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) -# Ship the OpenSSL shared libraries we link against next to iotdb_session so the -# packaged SDK is self-contained on machines without a system OpenSSL. +# Ship the Tongsuo shared libraries we link against next to iotdb_session so the +# packaged SDK is self-contained on machines without a system SSL library. if(WITH_SSL) iotdb_install_openssl_runtime() endif() @@ -307,7 +319,6 @@ install(FILES "${CMAKE_BINARY_DIR}/package-metadata/VERSION" "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt" DESTINATION .) - if(BUILD_TESTING) enable_testing() add_subdirectory(test) diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index a88293738fdb..30f85eae80cc 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -300,8 +300,8 @@ so they require glibc 2.28 or newer on the deployment host. | ppc64le | `quay.io/pypa/manylinux_2_28_ppc64le` | | s390x | `quay.io/pypa/manylinux_2_28_s390x` | -Thrift **0.23.0** is compiled from source during the CMake configure step (see -`cmake/FetchThrift.cmake`). Older releases that used pre-built +Thrift commit **`6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242`** (post-0.23.0) is compiled from +source during the CMake configure step (see `cmake/FetchThrift.cmake`). Older releases that used pre-built `iotdb-tools-thrift` Maven artifacts and `-Diotdb-tools-thrift.version=...` for glibc/MSVC compatibility apply only to the **legacy** client-cpp build; with the current CMake build, compatibility is determined by the **compiler @@ -378,15 +378,16 @@ etc. directly. | Option | Default | Purpose | |-----------------------|----------------------------------|----------------------------------------------------------------------------------------------------------| -| `WITH_SSL` | `ON` | Link against OpenSSL and bundle its runtime libraries. See *SSL* below. | +| `WITH_SSL` | `ON` | Link against Tongsuo (OpenSSL-compatible) and bundle its runtime libraries. See *SSL* below. | | `BUILD_TESTING` | `OFF` (Maven sets `ON` for verify) | Build Catch2 IT executables (Catch2 v2.13.7 header downloaded at configure time). | | `CATCH2_INCLUDE_DIR` | (unset) | Pre-downloaded Catch2 include dir (Maven sets this under `target/test/catch2`). | | `IOTDB_OFFLINE` | `OFF` | Disallow any network access during configure. | | `IOTDB_DEPS_DIR` | `/third-party` | Override the local tarball cache directory. | | `BOOST_VERSION` | `1.60.0` (`1.84.0` on macOS) | Boost version that CMake will look for / download. | -| `THRIFT_VERSION` | `0.23.0` | Apache Thrift version to build from source. | +| `THRIFT_GIT_COMMIT` | `6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242` | Apache Thrift git commit to build from source. | +| `TONGSUO_GIT_REF` | `8.4.0` | Immutable Tongsuo git tag built from source when `WITH_SSL=ON`. | +| `TONGSUO_TARBALL_SHA256` | `57c274…fc975` | Expected SHA256 of the Tongsuo source tarball (verified on download). | | `BOOST_ROOT` | (unset) | Existing Boost install to reuse, equivalent to `-Dboost.include.dir=...` from the legacy build. | -| `OPENSSL_ROOT_DIR` | (unset) | Existing OpenSSL install when `WITH_SSL=ON`. | | `CMAKE_INSTALL_PREFIX`| `/install` | Install location. | | `CMAKE_BUILD_TYPE` | `Release` | Single-config generator build type. Use `Debug` to produce a debug library. | @@ -427,17 +428,17 @@ cmake --build build --config Release --target install | Platform | Required files | |------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| - | `linux/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (and `openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is present) | - | `mac/` | `thrift-0.23.0.tar.gz`, `boost_1_84_0.tar.gz` (newer Boost for Xcode/Clang; Apple ships m4/flex/bison; `openssl-3.5.0.tar.gz` optional) | - | `windows/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz` (Boost headers only - no `b2` build required for `iotdb_session`) | + | `linux/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4.0.tar.gz` | + | `mac/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_84_0.tar.gz`, `tongsuo-8.4.0.tar.gz` (Apple ships m4/flex/bison) | + | `windows/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `tongsuo-8.4.0.tar.gz` (Boost headers only - no `b2` build required for `iotdb_session`) | Reference URLs (the configure step uses the same): - - Apache Thrift 0.23.0: + - Apache Thrift (git): - Boost 1.60.0: - GNU m4 1.4.19: - GNU flex 2.6.4: - GNU bison 3.8: - - OpenSSL 3.5.0: + - Tongsuo 8.4.0: 2. Run the build with offline mode enabled: @@ -460,8 +461,8 @@ CI environments can share a single cache by setting ### Linux -- Tested with GCC 7+ and Clang 9+. Anything that can compile Apache Thrift - 0.23.0 works. +- Tested with GCC 7+ and Clang 9+. Anything that can compile the pinned Apache + Thrift commit works. - Build deps that must already exist on the host (only required when CMake auto-builds m4/flex/bison from tarball): `make`, `autoconf`, `gcc`, plus the standard C/C++ toolchain. `sudo` is **not** required; @@ -492,11 +493,10 @@ Prerequisites: 2. **flex / bison.** Install and rename `win_flex.exe`→`flex.exe`, `win_bison.exe`→`bison.exe` on `PATH`. -3. **OpenSSL** *(`WITH_SSL=ON` is the default)*: install OpenSSL — e.g. - `choco install openssl`, or a Win64 OpenSSL installer from - — then pass - `-DOPENSSL_ROOT_DIR=...` to CMake if it is not auto-detected. Pass - `-DWITH_SSL=OFF` to build without SSL. +3. **Perl** (for building Tongsuo when `WITH_SSL=ON`). +4. **Tongsuo / SSL** *(`WITH_SSL=ON` is the default)*: Tongsuo 8.4.0 is + always built from source (requires Perl and `nmake` from the VS Developer + Command Prompt). Pass `-DWITH_SSL=OFF` to build without SSL. On Windows the SDK ships as **`iotdb_session.dll`** plus an import library **`iotdb_session.lib`**, built with **`/MD`** (dynamic CRT, same as a @@ -509,27 +509,112 @@ the GNU autotools tarballs assume a POSIX shell environment. ## SSL -`iotdb_session` builds **with OpenSSL by default** (`WITH_SSL=ON`). Disable +`iotdb_session` builds **with SSL/TLS by default** (`WITH_SSL=ON`). Disable it with `-Dwith.ssl=OFF` (Maven) or `-DWITH_SSL=OFF` (standalone CMake). -OpenSSL **3.x** is used (Apache-2.0 licensed). Note that **OpenSSL 4.0 removed** -the legacy TLS-method APIs (`TLSv1_method`, `SSLv3_method`, …) that Apache -Thrift's `TSSLSocket` still calls, so install/point at a 3.x build, not 4.0. - -CMake calls `find_package(OpenSSL)` and uses the system OpenSSL it finds. Its -shared libraries are **bundled into the package `lib/` directory** (next to +[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0** is +**always built from source** during configure (Apache-2.0 licensed, +OpenSSL-compatible API). It adds Chinese commercial cipher and TLCP protocol +support on top of standard TLS. The resulting `libssl` / `libcrypto` shared +libraries are **bundled into the package `lib/` directory** (next to `iotdb_session`, which records an `$ORIGIN`/`@loader_path` runtime path) so the published SDK is self-contained. -Fallbacks: +Host prerequisites when `WITH_SSL=ON`: + +- **Linux / macOS** – `perl`, `make`, and a C compiler (Tongsuo `./config`). +- **Windows** – Perl (e.g. Strawberry Perl) and `nmake` from the Visual Studio + Developer Command Prompt. + +### Client SSL / TLCP configuration + +The C++ client mirrors the Java Session API. Use **PKCS12** (`.p12` / `.pfx`) +for `trustStore` and `keyStore`. **JKS is not supported** — convert to PKCS12 +first. PEM CA files are supported via `trustStore` (PEM path) or the legacy +`trustCertFilePath()` setter when `trustStore` is empty. + +| API field | C++ meaning | Notes | +|-----------|-------------|-------| +| `trustStore` | Server trust material (PKCS#12 or PEM CA file) | Not JKS; `.p12`/`.pfx` = PKCS#12 | +| `keyStore` | Client identity (PKCS#12 or PEM cert+key) | TLCP mutual auth needs dual-cert PKCS#12 | +| `trustCertFilePath` | Legacy PEM CA path | Used only when `trustStore` is unset | + +OpenSSL-style PEM users can point `trustStore` at a `.pem` CA bundle, or use +`trustCertFilePath()` for the same PEM file without PKCS#12 wrapping. + +**TLS one-way (server authentication):** -- **Linux / macOS** – when no system OpenSSL is found (or - `-DIOTDB_OPENSSL_FROM_SOURCE=ON`, which the Linux packaging build uses so the - AlmaLinux 8 baseline's OpenSSL 1.1.1 is never redistributed), build - `openssl-3.5.0.tar.gz` from source as **shared** libraries and bundle them. -- **Windows** – fail with a friendly message; install a prebuilt OpenSSL 3.x - (e.g. the FireDaemon or slproweb 3.5.x zip) and set `-DOPENSSL_ROOT_DIR=...`. - Building OpenSSL from source via MSVC is out of scope. +```cpp +#include "SessionBuilder.h" + +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLS mutual authentication:** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/keystore.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +**TLCP one-way (NTLS, GM/T):** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP mutual authentication** (dual SM2 certificates in PKCS12 `keyStore`): + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client-dual.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +The legacy `trustCertFilePath()` setter still works as an alias for a PEM CA +file when `trustStore` is not set. + +**C API** (configure before `ts_session_open` / `ts_table_session_open`): + +```c +CSession* session = ts_session_new("127.0.0.1", 6667, "root", "root"); +ts_session_set_use_ssl(session, true); +ts_session_set_ssl_protocol(session, "TLCP"); +ts_session_set_trust_store(session, "/path/to/ca.p12", "thrift"); +ts_session_set_key_store(session, "/path/to/client-dual.p12", "thrift"); +ts_session_open(session); +``` ## Tests diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index 7c4326d661da..aeccfd6bc843 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -243,11 +243,77 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 | `BOOST_INCLUDEDIR` | `boost.include.dir` | | `CMAKE_BUILD_TYPE` | `cmake.build.type`,例如 `-Dcmake.build.type=Debug` | -SSL 默认开启(`WITH_SSL=ON`)。所捆绑的 Apache Thrift 0.23 同时支持 OpenSSL 1.x -与 3.x,因此直接使用系统的 OpenSSL(任意版本)。CMake 通过 `find_package(OpenSSL)` -解析系统 OpenSSL,找不到时回退到从源码构建 OpenSSL 3.5.0;并会把所用的 OpenSSL -动态库一并复制到产物 `lib/` 目录。Windows 可用 `choco install openssl` 安装。 +SSL 默认开启(`WITH_SSL=ON`)。配置阶段**始终从源码构建** +[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0** +(OpenSSL 兼容 API,Apache-2.0,支持国密/TLCP),并把 `libssl`/`libcrypto` +动态库复制到产物 `lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。 + +### 客户端 SSL / TLCP 配置 + +C++ 客户端 API 与 Java Session 对齐。`trustStore` 与 `keyStore` 请使用 +**PKCS12**(`.p12` / `.pfx`)。**不支持 JKS**,需先转换为 PKCS12。PEM 格式 +CA 可通过 `trustStore`(指向 `.pem` 文件)或遗留的 `trustCertFilePath()` 配置 +(仅当 `trustStore` 为空时生效)。 + +| API 字段 | C++ 含义 | 说明 | +|----------|----------|------| +| `trustStore` | 服务端信任材料(PKCS#12 或 PEM CA) | 非 JKS;`.p12`/`.pfx` 表示 PKCS#12 | +| `keyStore` | 客户端身份(PKCS#12 或 PEM 证书+私钥) | TLCP 双向认证需双证书 PKCS#12 | +| `trustCertFilePath` | 遗留 PEM CA 路径 | 仅 `trustStore` 未设置时使用 | + +**TLS 单向认证:** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore("/path/to/truststore.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP 单向认证(国密 NTLS):** + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->build(); +``` + +**TLCP 双向认证**(PKCS12 `keyStore` 内含 SM2 签名/加密双证书): + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client-dual.p12") + ->keyStorePwd("thrift") + ->build(); +``` + +旧版 `trustCertFilePath()` 在未设置 `trustStore` 时仍可作为 PEM CA 路径使用。 + +**C API**(在 `ts_session_open` / `ts_table_session_open` 之前配置): + +```c +ts_session_set_use_ssl(session, true); +ts_session_set_ssl_protocol(session, "TLCP"); +ts_session_set_trust_store(session, "/path/to/ca.p12", "thrift"); +ts_session_set_key_store(session, "/path/to/client-dual.p12", "thrift"); +``` + Debug 构建请在配置阶段传入 `-DCMAKE_BUILD_TYPE=Debug`。Windows 使用 Visual Studio 生成器时也需要传入该选项,以便内置 Thrift 静态库使用 Debug MSVC 运行时; 随后用 `cmake --build build --config Debug --target install` 构建安装。 diff --git a/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake b/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake index 866cc553954c..7b7589ac5e73 100644 --- a/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake +++ b/iotdb-client/client-cpp/cmake/FetchBuildTools.cmake @@ -266,7 +266,7 @@ if(BISON_EXECUTABLE) if(_bison_ver AND _bison_ver VERSION_LESS _bison_min_version) message(STATUS "[BuildTools] system bison ${_bison_ver} < ${_bison_min_version} " - "(too old for Thrift ${THRIFT_VERSION}); building ${BISON_VERSION} from source") + "(too old for Thrift ${THRIFT_GIT_COMMIT}); building ${BISON_VERSION} from source") unset(BISON_EXECUTABLE CACHE) endif() endif() diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index aaf41b89be41..e7aaa2ed6f16 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -18,81 +18,73 @@ # ============================================================================= # FetchOpenSSL.cmake (only included when WITH_SSL=ON) # -# Apache Thrift 0.23 (bundled by this client) builds against OpenSSL 1.x and 3.x, -# so any system OpenSSL is used as-is, whatever its version. -# -# Resolution order: -# 1. find_package(OpenSSL) - any system / vendor install is taken as-is. -# 2. On Linux/macOS, when no system OpenSSL is present: -# use tarball ${IOTDB_OS_DEPS_DIR}/openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz -# or download from openssl.org when not in offline mode, then -# ./config && make && make install_sw into ${CMAKE_BINARY_DIR}/_deps/openssl. -# 3. On Windows: emit a FATAL_ERROR asking for a prebuilt OpenSSL; building -# OpenSSL from source on MSVC is out of scope. +# Builds Tongsuo (OpenSSL-compatible, Apache-2.0) from source for Thrift +# TSSLSocket and iotdb_session. Tongsuo adds Chinese commercial cipher / TLCP +# support on top of the standard TLS stack. # # Side effects: -# Defines imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package -# so callers can just link against them. +# Sets OPENSSL_ROOT_DIR to the local Tongsuo install tree, then defines +# imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package so callers +# can link against them unchanged. # ============================================================================= -# Version built from source when no system OpenSSL is found. Named distinctly -# from find_package's OPENSSL_VERSION output variable to avoid collisions. -set(OPENSSL_FALLBACK_VERSION "3.5.0" - CACHE STRING "OpenSSL version built from source when no system OpenSSL is found") - -# Build OpenSSL from source even if a system one exists. Used by the Linux -# packaging build, whose AlmaLinux 8 baseline ships OpenSSL 1.1.1 (EOL, not -# Apache-2.0, must not be redistributed) - we build 3.x there instead. -option(IOTDB_OPENSSL_FROM_SOURCE - "Ignore any system OpenSSL and build OpenSSL ${OPENSSL_FALLBACK_VERSION} from source" OFF) - -if(NOT IOTDB_OPENSSL_FROM_SOURCE) - find_package(OpenSSL QUIET) - if(OpenSSL_FOUND) - message(STATUS "[OpenSSL] using system OpenSSL ${OPENSSL_VERSION}") - return() - endif() -endif() - -if(WIN32) - message(FATAL_ERROR - "[OpenSSL] WITH_SSL=ON but no OpenSSL was found on Windows. " - "Please install a prebuilt OpenSSL (e.g. 'choco install openssl'), " - "then re-run the configure step with -DOPENSSL_ROOT_DIR=. " - "Pass -DWITH_SSL=OFF to build without SSL.") +# --- Build Tongsuo ${TONGSUO_GIT_REF} from source --- +if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]{7,40}$") + set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") + set(_tongsuo_url "https://github.com/Tongsuo-Project/Tongsuo/archive/${TONGSUO_GIT_REF}.tar.gz") +elseif(TONGSUO_GIT_REF MATCHES "^[0-9]+\\.[0-9]") + set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") + set(_tongsuo_url + "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/${TONGSUO_GIT_REF}.tar.gz") +else() + set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") + set(_tongsuo_url + "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/heads/${TONGSUO_GIT_REF}.tar.gz") endif() -# --- Linux / macOS: build OpenSSL ${OPENSSL_FALLBACK_VERSION} from source - -set(_ossl_tarname "openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz") -set(_ossl_tarball "${IOTDB_OS_DEPS_DIR}/${_ossl_tarname}") +set(_tongsuo_tarname "tongsuo-${TONGSUO_GIT_REF}.tar.gz") +set(_tongsuo_tarball "${IOTDB_OS_DEPS_DIR}/${_tongsuo_tarname}") -if(NOT EXISTS "${_ossl_tarball}") +if(NOT EXISTS "${_tongsuo_tarball}") if(IOTDB_OFFLINE) message(FATAL_ERROR - "[OpenSSL] IOTDB_OFFLINE=ON but ${_ossl_tarname} is missing in ${IOTDB_OS_DEPS_DIR}.") + "[Tongsuo] IOTDB_OFFLINE=ON but ${_tongsuo_tarname} is missing in ${IOTDB_OS_DEPS_DIR}.") endif() - set(_ossl_url "https://www.openssl.org/source/${_ossl_tarname}") - message(STATUS "[OpenSSL] downloading ${_ossl_url}") - file(DOWNLOAD "${_ossl_url}" "${_ossl_tarball}" - SHOW_PROGRESS TLS_VERIFY ON STATUS _st) + message(STATUS "[Tongsuo] downloading ${_tongsuo_url}") + file(DOWNLOAD "${_tongsuo_url}" "${_tongsuo_tarball}" + SHOW_PROGRESS TLS_VERIFY ON + TIMEOUT 600 + STATUS _st) list(GET _st 0 _code) if(NOT _code EQUAL 0) list(GET _st 1 _msg) - file(REMOVE "${_ossl_tarball}") - message(FATAL_ERROR "[OpenSSL] download failed: ${_msg}") + file(REMOVE "${_tongsuo_tarball}") + message(FATAL_ERROR "[Tongsuo] download failed: ${_msg}") + endif() +endif() + +if(TONGSUO_TARBALL_SHA256) + file(SHA256 "${_tongsuo_tarball}" _tongsuo_actual_sha256) + string(TOLOWER "${TONGSUO_TARBALL_SHA256}" _tongsuo_expected_sha256) + string(TOLOWER "${_tongsuo_actual_sha256}" _tongsuo_actual_sha256) + if(NOT _tongsuo_actual_sha256 STREQUAL _tongsuo_expected_sha256) + file(REMOVE "${_tongsuo_tarball}") + message(FATAL_ERROR + "[Tongsuo] tarball SHA256 mismatch for ${_tongsuo_tarname}: " + "expected ${_tongsuo_expected_sha256}, got ${_tongsuo_actual_sha256}") endif() endif() -set(_ossl_root "${CMAKE_BINARY_DIR}/_deps/openssl") -set(_ossl_src "${_ossl_root}/src/openssl-${OPENSSL_FALLBACK_VERSION}") -set(_ossl_inst "${_ossl_root}/install") -set(_ossl_stamp "${_ossl_root}/.built-${OPENSSL_FALLBACK_VERSION}") +set(_tongsuo_root "${CMAKE_BINARY_DIR}/_deps/tongsuo") +set(_tongsuo_src "${_tongsuo_root}/src/${_tongsuo_extracted_dir}") +set(_tongsuo_inst "${_tongsuo_root}/install") +set(_tongsuo_stamp "${_tongsuo_root}/.built-${TONGSUO_GIT_REF}") -if(NOT EXISTS "${_ossl_stamp}") - file(REMOVE_RECURSE "${_ossl_root}/src") - file(MAKE_DIRECTORY "${_ossl_root}/src") - message(STATUS "[OpenSSL] extracting ${_ossl_tarball}") - file(ARCHIVE_EXTRACT INPUT "${_ossl_tarball}" DESTINATION "${_ossl_root}/src") +if(NOT EXISTS "${_tongsuo_stamp}") + file(REMOVE_RECURSE "${_tongsuo_root}/src") + file(MAKE_DIRECTORY "${_tongsuo_root}/src") + message(STATUS "[Tongsuo] extracting ${_tongsuo_tarball}") + file(ARCHIVE_EXTRACT INPUT "${_tongsuo_tarball}" DESTINATION "${_tongsuo_root}/src") include(ProcessorCount) ProcessorCount(_jobs) @@ -100,38 +92,196 @@ if(NOT EXISTS "${_ossl_stamp}") set(_jobs 1) endif() - message(STATUS "[OpenSSL] configuring -> ${_ossl_inst}") - # ./config auto-detects the platform target. Build SHARED libraries - # (libssl.so.3 / libcrypto.so.3) so they can be bundled next to - # libiotdb_session and shipped as the SDK's OpenSSL runtime. - execute_process( - COMMAND ./config --prefix=${_ossl_inst} --openssldir=${_ossl_inst}/ssl shared - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] config failed (rc=${_rc})") + if(WIN32) + # Git Bash ships a minimal MSYS perl that lacks modules required by + # Tongsuo/OpenSSL Configure (e.g. Locale::Maketext::Simple). Prefer + # Strawberry Perl installed by CI (choco) or local dev machines. + set(_strawberry_perl "C:/Strawberry/perl/bin/perl.exe") + if(EXISTS "${_strawberry_perl}") + set(PERL_EXECUTABLE "${_strawberry_perl}") + else() + find_program(PERL_EXECUTABLE NAMES perl.exe perl REQUIRED) + endif() + message(STATUS "[Tongsuo] using Perl: ${PERL_EXECUTABLE}") + find_program(NMAKE_EXECUTABLE nmake) + if(NOT NMAKE_EXECUTABLE AND CMAKE_CXX_COMPILER) + get_filename_component(_msvc_bin_dir "${CMAKE_CXX_COMPILER}" DIRECTORY) + find_program(NMAKE_EXECUTABLE nmake PATHS "${_msvc_bin_dir}" NO_DEFAULT_PATH) + endif() + if(NOT NMAKE_EXECUTABLE AND DEFINED ENV{VCINSTALLDIR}) + file(GLOB _nmake_candidates "$ENV{VCINSTALLDIR}/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe") + if(_nmake_candidates) + list(GET _nmake_candidates 0 NMAKE_EXECUTABLE) + endif() + endif() + if(NOT NMAKE_EXECUTABLE) + file(GLOB _nmake_candidates + "C:/Program Files (x86)/Microsoft Visual Studio/2017/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe" + "C:/Program Files/Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe" + "C:/Program Files/Microsoft Visual Studio/18/*/VC/Tools/MSVC/*/bin/Hostx64/x64/nmake.exe") + if(_nmake_candidates) + list(SORT _nmake_candidates COMPARE NATURAL ORDER DESCENDING) + list(GET _nmake_candidates 0 NMAKE_EXECUTABLE) + endif() + endif() + if(NOT NMAKE_EXECUTABLE) + message(FATAL_ERROR "[Tongsuo] nmake not found (install VS Build Tools or run from Developer Command Prompt)") + endif() + message(STATUS "[Tongsuo] using nmake: ${NMAKE_EXECUTABLE}") + set(_vcvars "") + if(CMAKE_CXX_COMPILER) + get_filename_component(_cl_exe "${CMAKE_CXX_COMPILER}" REALPATH) + set(_vc_dir "${_cl_exe}") + foreach(_unused RANGE 6) + get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) + endforeach() + set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + elseif(DEFINED ENV{VCINSTALLDIR}) + set(_vcvars "$ENV{VCINSTALLDIR}/Auxiliary/Build/vcvars64.bat") + else() + get_filename_component(_nmake_dir "${NMAKE_EXECUTABLE}" DIRECTORY) + set(_vc_dir "${_nmake_dir}") + foreach(_unused RANGE 6) + get_filename_component(_vc_dir "${_vc_dir}" DIRECTORY) + endforeach() + set(_vcvars "${_vc_dir}/Auxiliary/Build/vcvars64.bat") + endif() + if(NOT EXISTS "${_vcvars}") + message(FATAL_ERROR "[Tongsuo] vcvars64.bat not found (CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})") + endif() + file(TO_NATIVE_PATH "${_vcvars}" _vcvars_native) + file(TO_NATIVE_PATH "${NMAKE_EXECUTABLE}" _nmake_native) + file(TO_NATIVE_PATH "${_tongsuo_src}" _tongsuo_src_native) + set(_nmake_build_bat "${_tongsuo_root}/tongsuo-nmake-build.bat") + set(_nmake_install_bat "${_tongsuo_root}/tongsuo-nmake-install.bat") + file(WRITE "${_nmake_build_bat}" "@echo off\r\n") + file(APPEND "${_nmake_build_bat}" "call \"${_vcvars_native}\" amd64\r\n") + file(APPEND "${_nmake_build_bat}" "if errorlevel 1 exit /b 1\r\n") + file(APPEND "${_nmake_build_bat}" "cd /d \"${_tongsuo_src_native}\"\r\n") + file(APPEND "${_nmake_build_bat}" "\"${_nmake_native}\"\r\n") + file(APPEND "${_nmake_build_bat}" "exit /b %ERRORLEVEL%\r\n") + file(WRITE "${_nmake_install_bat}" "@echo off\r\n") + file(APPEND "${_nmake_install_bat}" "call \"${_vcvars_native}\" amd64\r\n") + file(APPEND "${_nmake_install_bat}" "if errorlevel 1 exit /b 1\r\n") + file(APPEND "${_nmake_install_bat}" "cd /d \"${_tongsuo_src_native}\"\r\n") + file(APPEND "${_nmake_install_bat}" "\"${_nmake_native}\" install_sw\r\n") + file(APPEND "${_nmake_install_bat}" "exit /b %ERRORLEVEL%\r\n") + set(_tongsuo_target "VC-WIN64A") + message(STATUS "[Tongsuo] configuring (${_tongsuo_target}) -> ${_tongsuo_inst}") + execute_process( + COMMAND "${PERL_EXECUTABLE}" Configure enable-ntls no-asm ${_tongsuo_target} + --prefix=${_tongsuo_inst} + --openssldir=${_tongsuo_inst}/ssl + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] Configure failed (rc=${_rc})") + endif() + + message(STATUS "[Tongsuo] building") + execute_process( + COMMAND "${_nmake_build_bat}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] nmake failed (rc=${_rc})") + endif() + + execute_process( + COMMAND "${_nmake_install_bat}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] nmake install_sw failed (rc=${_rc})") + endif() + else() + find_program(PERL_EXECUTABLE NAMES perl REQUIRED) + message(STATUS "[Tongsuo] using Perl: ${PERL_EXECUTABLE}") + set(_tongsuo_config_args + --prefix=${_tongsuo_inst} + --openssldir=${_tongsuo_inst}/ssl + shared + enable-ntls) + # Assembly optimizations often fail on macOS CI toolchains; match the + # Windows VC-WIN64A build which already passes no-asm. + if(APPLE) + list(APPEND _tongsuo_config_args no-asm) + endif() + message(STATUS "[Tongsuo] configuring -> ${_tongsuo_inst}") + execute_process( + COMMAND "${PERL_EXECUTABLE}" ./config ${_tongsuo_config_args} + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] config failed (rc=${_rc})") + endif() + + message(STATUS "[Tongsuo] building (-j${_jobs})") + execute_process( + COMMAND make -j${_jobs} + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] make failed (rc=${_rc})") + endif() + + execute_process( + COMMAND make install_sw + WORKING_DIRECTORY "${_tongsuo_src}" + RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(FATAL_ERROR "[Tongsuo] make install_sw failed (rc=${_rc})") + endif() endif() + file(TOUCH "${_tongsuo_stamp}") +endif() + +set(OPENSSL_ROOT_DIR "${_tongsuo_inst}" CACHE PATH "Tongsuo install root" FORCE) +set(OPENSSL_INCLUDE_DIR "${_tongsuo_inst}/include" CACHE PATH "Tongsuo headers" FORCE) +set(OPENSSL_USE_STATIC_LIBS OFF) - message(STATUS "[OpenSSL] building (-j${_jobs})") - execute_process( - COMMAND make -j${_jobs} - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] make failed (rc=${_rc})") +if(WIN32) + # MSVC needs FindOpenSSL imported targets (IMPORTED_IMPLIB + DLL). Hand-rolled + # SHARED IMPORTED targets break the link line (LNK1104: OpenSSL::SSL-NOTFOUND.obj). + find_package(OpenSSL REQUIRED) +elseif(APPLE) + # macOS CI runners ship Homebrew/Xcode OpenSSL headers on the default include + # path; find_package would satisfy version checks but still compile against the + # wrong headers. Link against the bundled Tongsuo libs and route + # through generated wrapper headers (see TongsuoOpenSslHeaders.cmake). + find_library(_iotdb_tongsuo_ssl NAMES ssl libssl + PATHS "${_tongsuo_inst}/lib" "${_tongsuo_inst}/lib64" + NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_library(_iotdb_tongsuo_crypto NAMES crypto libcrypto + PATHS "${_tongsuo_inst}/lib" "${_tongsuo_inst}/lib64" + NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + if(NOT _iotdb_tongsuo_ssl OR NOT _iotdb_tongsuo_crypto) + message(FATAL_ERROR + "[Tongsuo] libssl/libcrypto not found under ${_tongsuo_inst}/lib") endif() - execute_process( - COMMAND make install_sw - WORKING_DIRECTORY "${_ossl_src}" - RESULT_VARIABLE _rc) - if(NOT _rc EQUAL 0) - message(FATAL_ERROR "[OpenSSL] make install_sw failed (rc=${_rc})") + if(NOT TARGET OpenSSL::Crypto) + add_library(OpenSSL::Crypto SHARED IMPORTED) endif() - file(TOUCH "${_ossl_stamp}") + set_target_properties(OpenSSL::Crypto PROPERTIES + IMPORTED_LOCATION "${_iotdb_tongsuo_crypto}" + INTERFACE_INCLUDE_DIRECTORIES "${_tongsuo_inst}/include") + + if(NOT TARGET OpenSSL::SSL) + add_library(OpenSSL::SSL SHARED IMPORTED) + endif() + set_target_properties(OpenSSL::SSL PROPERTIES + IMPORTED_LOCATION "${_iotdb_tongsuo_ssl}" + INTERFACE_INCLUDE_DIRECTORIES "${_tongsuo_inst}/include" + INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + + set(OPENSSL_SSL_LIBRARY "${_iotdb_tongsuo_ssl}" CACHE FILEPATH "" FORCE) + set(OPENSSL_CRYPTO_LIBRARY "${_iotdb_tongsuo_crypto}" CACHE FILEPATH "" FORCE) + set(OPENSSL_VERSION_MAJOR 3 CACHE STRING "" FORCE) + + include(TongsuoOpenSslHeaders) + iotdb_setup_tongsuo_openssl_headers("${_tongsuo_inst}/include") +else() + find_package(OpenSSL REQUIRED) endif() -set(OPENSSL_ROOT_DIR "${_ossl_inst}" CACHE PATH "OpenSSL root" FORCE) -set(OPENSSL_USE_STATIC_LIBS OFF) -find_package(OpenSSL REQUIRED) -message(STATUS "[OpenSSL] built locally (shared) at ${OPENSSL_ROOT_DIR}") +message(STATUS "[Tongsuo] built from source (shared) at ${OPENSSL_ROOT_DIR}") +message(STATUS "[Tongsuo] OPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR}") diff --git a/iotdb-client/client-cpp/cmake/FetchThrift.cmake b/iotdb-client/client-cpp/cmake/FetchThrift.cmake index d69b2a47ad9e..7ac7dd6838bf 100644 --- a/iotdb-client/client-cpp/cmake/FetchThrift.cmake +++ b/iotdb-client/client-cpp/cmake/FetchThrift.cmake @@ -41,7 +41,7 @@ include(ExternalProject) -set(_thrift_dirname "thrift-${THRIFT_VERSION}") +set(_thrift_dirname "thrift-${THRIFT_GIT_COMMIT}") set(_thrift_tarname "${_thrift_dirname}.tar.gz") # --------------------------------------------------------------------------- @@ -54,10 +54,13 @@ if(NOT EXISTS "${_thrift_tarball}") "[Thrift] IOTDB_OFFLINE=ON but ${_thrift_tarname} is missing in " "${IOTDB_OS_DEPS_DIR}.") endif() - set(_thrift_url "https://archive.apache.org/dist/thrift/${THRIFT_VERSION}/${_thrift_tarname}") + set(_thrift_url + "https://github.com/apache/thrift/archive/${THRIFT_GIT_COMMIT}.tar.gz") message(STATUS "[Thrift] downloading ${_thrift_url}") file(DOWNLOAD "${_thrift_url}" "${_thrift_tarball}" - SHOW_PROGRESS TLS_VERIFY ON STATUS _thrift_dl) + SHOW_PROGRESS TLS_VERIFY ON + TIMEOUT 600 + STATUS _thrift_dl) list(GET _thrift_dl 0 _code) if(NOT _code EQUAL 0) list(GET _thrift_dl 1 _msg) @@ -73,7 +76,7 @@ set(_thrift_root "${CMAKE_BINARY_DIR}/_deps/thrift") set(_thrift_src "${_thrift_root}/src/${_thrift_dirname}") set(_thrift_build "${_thrift_root}/build") set(_thrift_install "${_thrift_root}/install") -set(_thrift_marker "${_thrift_root}/.extracted-${THRIFT_VERSION}") +set(_thrift_marker "${_thrift_root}/.extracted-${THRIFT_GIT_COMMIT}") set(_thrift_build_config "Release") if(MSVC AND CMAKE_BUILD_TYPE) @@ -89,12 +92,27 @@ if(NOT EXISTS "${_thrift_marker}") file(TOUCH "${_thrift_marker}") endif() +# GitHub archives use thrift-, release tarballs use thrift-. +if(NOT EXISTS "${_thrift_src}/CMakeLists.txt") + file(GLOB _thrift_extracted "${_thrift_root}/src/thrift-*") + list(LENGTH _thrift_extracted _thrift_extracted_count) + if(_thrift_extracted_count EQUAL 1) + list(GET _thrift_extracted 0 _thrift_found) + if(NOT _thrift_found STREQUAL _thrift_src) + message(STATUS "[Thrift] normalizing extracted dir ${_thrift_found} -> ${_thrift_src}") + file(RENAME "${_thrift_found}" "${_thrift_src}") + endif() + endif() +endif() + if(NOT EXISTS "${_thrift_src}/CMakeLists.txt") message(FATAL_ERROR "[Thrift] could not find ${_thrift_src}/CMakeLists.txt after " "extracting ${_thrift_tarball}.") endif() +include("${CMAKE_CURRENT_LIST_DIR}/PatchThriftSsl.cmake") + # --------------------------------------------------------------------------- # ExternalProject_Add: build thrift at *configure* time so the produced # binary / library can immediately drive code generation and linking. @@ -138,7 +156,7 @@ endif() if(WITH_SSL) list(APPEND _thrift_cmake_args "-DWITH_OPENSSL=ON") - # Build Thrift's TSSLSocket against the same OpenSSL that iotdb_session links + # Build Thrift's TSSLSocket against the same SSL library that iotdb_session links # and bundles, so the runtime libraries match. find_package does not set # OPENSSL_ROOT_DIR itself, so derive it from the resolved include dir. if(OPENSSL_ROOT_DIR) @@ -169,7 +187,7 @@ if(WITH_SSL) else() set(_thrift_ssl_stamp "-nossl") endif() -set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_VERSION}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}") +set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_GIT_COMMIT}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}-sslctx") if(NOT EXISTS "${_thrift_stamp}") file(MAKE_DIRECTORY "${_thrift_build}") message(STATUS "[Thrift] configuring ${_thrift_dirname}") diff --git a/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake b/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake new file mode 100644 index 000000000000..ff5394c1b30e --- /dev/null +++ b/iotdb-client/client-cpp/cmake/PatchThriftSsl.cmake @@ -0,0 +1,80 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# ============================================================================= +# PatchThriftSsl.cmake +# +# Extends the vendored Apache Thrift C++ SSL transport with SSLContextFactory and +# SSLContext(SSL_CTX*) so IoTDB can inject custom OpenSSL / NTLS contexts. +# ============================================================================= + +if(NOT WITH_SSL) + return() +endif() + +set(_thrift_ssl_header "${_thrift_src}/lib/cpp/src/thrift/transport/TSSLSocket.h") +set(_thrift_ssl_cpp "${_thrift_src}/lib/cpp/src/thrift/transport/TSSLSocket.cpp") +set(_thrift_ssl_patch_marker "${_thrift_root}/.patched-ssl-context-${THRIFT_GIT_COMMIT}") + +if(EXISTS "${_thrift_ssl_patch_marker}") + return() +endif() + +if(NOT EXISTS "${_thrift_ssl_header}") + message(FATAL_ERROR "[Thrift] cannot patch missing ${_thrift_ssl_header}") +endif() + +file(READ "${_thrift_ssl_header}" _thrift_ssl_header_content) +if(NOT _thrift_ssl_header_content MATCHES "SSLContextFactory") + if(NOT _thrift_ssl_header_content MATCHES "#include ") + string(REPLACE + "#include " + "#include \n#include " + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + endif() + string(REPLACE + "class SSLContext;" + "class SSLContext;\ntypedef std::function()> SSLContextFactory;" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + string(REPLACE + " TSSLSocketFactory(SSLProtocol protocol = SSLTLS);" + " TSSLSocketFactory(SSLProtocol protocol = SSLTLS);\n /**\n * Constructor\n *\n * @param contextFactory Function invoked during construction to return a custom OpenSSL context.\n */\n TSSLSocketFactory(const SSLContextFactory& contextFactory);" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + string(REPLACE + " SSLContext(const SSLProtocol& protocol = SSLTLS);" + " SSLContext(const SSLProtocol& protocol = SSLTLS);\n /**\n * Wrap an existing OpenSSL SSL_CTX.\n *\n * Takes ownership of @a ctx; the caller must not call SSL_CTX_free on it.\n */\n explicit SSLContext(SSL_CTX* ctx);" + _thrift_ssl_header_content "${_thrift_ssl_header_content}") + file(WRITE "${_thrift_ssl_header}" "${_thrift_ssl_header_content}") +endif() + +if(EXISTS "${_thrift_ssl_cpp}") + file(READ "${_thrift_ssl_cpp}" _thrift_ssl_cpp_content) + if(NOT _thrift_ssl_cpp_content MATCHES "SSLContext::SSLContext\\(SSL_CTX\\* ctx\\)") + string(REPLACE + "SSLContext::~SSLContext() {" + "SSLContext::SSLContext(SSL_CTX* ctx) : ctx_(ctx) {\n if (ctx_ == nullptr) {\n string errors;\n buildErrors(errors);\n throw TSSLException(\"SSL_CTX_new: null context\");\n }\n}\n\nSSLContext::~SSLContext() {" + _thrift_ssl_cpp_content "${_thrift_ssl_cpp_content}") + string(REPLACE + "TSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) {" + "TSSLSocketFactory::TSSLSocketFactory(const SSLContextFactory& contextFactory) : server_(false) {\n Guard guard(mutex_);\n if (count_ == 0) {\n if (!manualOpenSSLInitialization_) {\n didWeInitializeOpenSSL_ = true;\n initializeOpenSSL();\n }\n randomize();\n }\n count_++;\n ctx_ = contextFactory();\n}\n\nTSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) {" + _thrift_ssl_cpp_content "${_thrift_ssl_cpp_content}") + file(WRITE "${_thrift_ssl_cpp}" "${_thrift_ssl_cpp_content}") + endif() +endif() + +file(TOUCH "${_thrift_ssl_patch_marker}") +message(STATUS "[Thrift] applied SSLContextFactory patch to ${_thrift_ssl_header}") diff --git a/iotdb-client/client-cpp/cmake/TongsuoOpenSslHeaders.cmake b/iotdb-client/client-cpp/cmake/TongsuoOpenSslHeaders.cmake new file mode 100644 index 000000000000..c933624932c2 --- /dev/null +++ b/iotdb-client/client-cpp/cmake/TongsuoOpenSslHeaders.cmake @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# ============================================================================= +# Generate thin wrapper headers under openssl/ that include the bundled Tongsuo +# tree via absolute paths. macOS CI runners also ship OpenSSL-compatible headers +# in the Xcode SDK; angle-bracket includes can resolve there instead of Tongsuo +# and hide NTLS APIs even when -I points at the bundled install prefix. +# ============================================================================= + +function(iotdb_setup_tongsuo_openssl_headers _tongsuo_include_dir) + if(NOT IS_DIRECTORY "${_tongsuo_include_dir}/openssl") + message(FATAL_ERROR + "[Tongsuo] expected headers under ${_tongsuo_include_dir}/openssl") + endif() + + set(_wrap_dir "${CMAKE_BINARY_DIR}/generated/tongsuo-openssl-wrap") + set(_ossl_wrap "${_wrap_dir}/openssl") + file(MAKE_DIRECTORY "${_ossl_wrap}") + set(_ossl_root "${_tongsuo_include_dir}/openssl") + file(GLOB _ossl_headers RELATIVE "${_ossl_root}" "${_ossl_root}/*.h") + foreach(_header ${_ossl_headers}) + file(WRITE "${_ossl_wrap}/${_header}" + "#pragma once\n#include \"${_ossl_root}/${_header}\"\n") + endforeach() + + if(NOT TARGET iotdb_tongsuo_openssl_wrap) + add_library(iotdb_tongsuo_openssl_wrap INTERFACE) + target_include_directories(iotdb_tongsuo_openssl_wrap BEFORE INTERFACE + "${_wrap_dir}") + endif() + set(IOTDB_TONGSUO_OPENSSL_WRAP_DIR "${_wrap_dir}" PARENT_SCOPE) +endfunction() diff --git a/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp b/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp index 80d1caadd353..5d2154193e2d 100644 --- a/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp +++ b/iotdb-client/client-cpp/examples/AlignedTimeseriesSessionExample.cpp @@ -415,6 +415,7 @@ int main() { session->close(); delete session; + session = nullptr; cout << "finished\n" << endl; return 0; diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index 4184199847f8..48f592475bef 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -61,21 +61,55 @@ else() INCLUDE_DIRECTORIES("${IOTDB_SDK_ROOT}/include") endif() -option(WITH_SSL "Build with SSL support" OFF) - -IF(WITH_SSL) - FIND_PACKAGE(OpenSSL REQUIRED) - IF(OpenSSL_FOUND) - MESSAGE(STATUS "OpenSSL found: ${OPENSSL_VERSION}") - INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) - ADD_DEFINITIONS(-DWITH_SSL=1) - ELSE() - MESSAGE(FATAL_ERROR "OpenSSL not found, but WITH_SSL is enabled") - ENDIF() -ELSE() - MESSAGE(STATUS "Building without SSL support") - ADD_DEFINITIONS(-DWITH_SSL=0) -ENDIF() +# Match the SDK default (WITH_SSL=ON in the main client build). When this +# directory is added via add_subdirectory(), the parent cache value wins. +option(WITH_SSL "Build with SSL/TLS support" ON) + +set(_iotdb_use_bundled_ssl OFF) +set(_iotdb_ssl_link_libs "") + +if(NOT _iotdb_examples_in_tree) + file(GLOB _iotdb_bundled_ssl_runtime + "${IOTDB_SDK_ROOT}/lib/libssl*.so*" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.so*" + "${IOTDB_SDK_ROOT}/lib/libssl*.dylib" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.dylib" + "${IOTDB_SDK_ROOT}/lib/libssl*.dll" + "${IOTDB_SDK_ROOT}/lib/libcrypto*.dll") + if(_iotdb_bundled_ssl_runtime) + set(_iotdb_use_bundled_ssl ON) + set(WITH_SSL ON CACHE BOOL "Build with SSL/TLS support" FORCE) + message(STATUS "Using bundled Tongsuo/OpenSSL-compatible libraries from ${IOTDB_SDK_ROOT}/lib") + endif() +endif() + +if(WITH_SSL) + if(_iotdb_examples_in_tree) + add_compile_definitions(WITH_SSL=1) + elseif(_iotdb_use_bundled_ssl) + add_compile_definitions(WITH_SSL=1) + if(UNIX) + find_library(_iotdb_ssl_lib NAMES ssl libssl + PATHS "${IOTDB_SDK_ROOT}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_library(_iotdb_crypto_lib NAMES crypto libcrypto + PATHS "${IOTDB_SDK_ROOT}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + if(_iotdb_ssl_lib AND _iotdb_crypto_lib) + set(_iotdb_ssl_link_libs "${_iotdb_ssl_lib}" "${_iotdb_crypto_lib}") + else() + message(FATAL_ERROR + "Bundled libssl/libcrypto not found under ${IOTDB_SDK_ROOT}/lib") + endif() + endif() + else() + message(FATAL_ERROR + "WITH_SSL=ON requires building inside the IoTDB client tree, or an SDK " + "that bundles libssl/libcrypto under ${IOTDB_SDK_ROOT}/lib. " + "Pass -DWITH_SSL=OFF only for SDKs built without SSL.") + endif() +else() + message(STATUS "Building without SSL support") + add_compile_definitions(WITH_SSL=0) +endif() if(NOT _iotdb_examples_in_tree) find_package(iotdb-session CONFIG QUIET @@ -118,35 +152,61 @@ set(_example_targets tree_example table_example) -# OpenSSL runtime libraries bundled in the SDK lib/ (libssl / libcrypto). When -# building against an unpacked package, copy them next to each example binary so -# the examples run without a system OpenSSL - libiotdb_session records them as -# NEEDED and resolves them via its $ORIGIN runtime path. -set(_iotdb_sdk_ssl_runtime "") -if(NOT _iotdb_examples_in_tree) - file(GLOB _iotdb_sdk_ssl_runtime - "${IOTDB_SDK_ROOT}/lib/libssl*.so*" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.so*" - "${IOTDB_SDK_ROOT}/lib/libssl*.dylib" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.dylib" - "${IOTDB_SDK_ROOT}/lib/libssl*.dll" - "${IOTDB_SDK_ROOT}/lib/libcrypto*.dll") +set(_it_plain_examples "") +set(_it_ssl_examples "") +set(_it_ntls_examples "") + +if(_iotdb_examples_in_tree) + ADD_EXECUTABLE(cpp_tree_example cpp_tree_example.cpp) + ADD_EXECUTABLE(cpp_table_example cpp_table_example.cpp) + + list(APPEND _example_targets + cpp_tree_example + cpp_table_example) + + set(_it_plain_examples + cpp_tree_example + cpp_table_example + tree_example + table_example) +endif() + +if(WITH_SSL AND (_iotdb_examples_in_tree OR _iotdb_use_bundled_ssl)) + ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) + ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) + ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) + ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) + + list(APPEND _example_targets + cpp_tls_example + cpp_ntls_example + tls_tree_example + c_ntls_example) + + if(_iotdb_examples_in_tree) + set(_it_ssl_examples + cpp_tls_example + tls_tree_example) + + set(_it_ntls_examples + cpp_ntls_example + c_ntls_example) + endif() endif() foreach(_t IN LISTS _example_targets) - IF(WITH_SSL) - TARGET_LINK_LIBRARIES(${_t} PRIVATE "${_iotdb_link_lib}" OpenSSL::SSL OpenSSL::Crypto) - ELSE() - TARGET_LINK_LIBRARIES(${_t} PRIVATE "${_iotdb_link_lib}") - ENDIF() + if(WITH_SSL AND _iotdb_ssl_link_libs) + target_link_libraries(${_t} PRIVATE "${_iotdb_link_lib}" ${_iotdb_ssl_link_libs}) + else() + target_link_libraries(${_t} PRIVATE "${_iotdb_link_lib}") + endif() IF(UNIX) TARGET_LINK_LIBRARIES(${_t} PRIVATE pthread) ENDIF() - # The packaged libiotdb_session records the bundled OpenSSL libs as DT_NEEDED; - # point the linker at the SDK lib/ so it can resolve them without a system - # OpenSSL present. - if(UNIX AND NOT _iotdb_examples_in_tree) + # The packaged libiotdb_session records the bundled SSL libs as DT_NEEDED; point + # the linker at the SDK lib/ so it can resolve them without a system install. + if(UNIX AND NOT _iotdb_examples_in_tree AND _iotdb_use_bundled_ssl) target_link_directories(${_t} PRIVATE "${IOTDB_SDK_ROOT}/lib") endif() @@ -162,22 +222,77 @@ foreach(_t IN LISTS _example_targets) COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMENT "Copy IoTDB runtime library next to ${_t}") + if(WIN32 AND WITH_SSL) + _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls) + foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_dll}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endforeach() + endif() elseif(EXISTS "${_iotdb_runtime}") add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_iotdb_runtime}" $ COMMENT "Copy IoTDB runtime library next to ${_t}") - foreach(_ssl_lib IN LISTS _iotdb_sdk_ssl_runtime) + foreach(_ssl_lib IN LISTS _iotdb_bundled_ssl_runtime) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_ssl_lib}" $ - COMMENT "Copy bundled OpenSSL runtime next to ${_t}") + COMMENT "Copy bundled SSL runtime next to ${_t}") endforeach() elseif(WIN32) message(WARNING "Missing ${_iotdb_runtime}; copy iotdb_session.dll manually before running ${_t}.") endif() endforeach() +if(_iotdb_examples_in_tree AND WITH_SSL AND IOTDB_EXAMPLES_REGISTER_TESTS) + file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../test/fixtures" _iotdb_example_fixtures_dir) + string(REPLACE "\\" "/" _iotdb_example_fixtures_dir_fwd "${_iotdb_example_fixtures_dir}") + foreach(_t IN LISTS _it_ssl_examples _it_ntls_examples) + target_compile_definitions(${_t} PRIVATE + IOTDB_TEST_FIXTURES_DIR="${_iotdb_example_fixtures_dir_fwd}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/../test/fixtures" + "$/fixtures" + COMMENT "Copy SSL test fixtures next to ${_t}") + endforeach() + foreach(_t IN LISTS _it_ntls_examples) + target_sources(${_t} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../test/cpp/SslTestFixtures.cpp") + target_include_directories(${_t} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../test/cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/rpc" + "${THRIFT_GEN_CPP_DIR}" + "${THRIFT_INCLUDE_DIR}") + if(BOOST_INCLUDE_DIR) + target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") + endif() + if(BOOST_INCLUDE_DIR) + target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") + endif() + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir_cmake}") + if(WIN32) + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl.exe" _iotdb_openssl_executable_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + target_compile_definitions(${_t} PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") + target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + else() + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}/bin/openssl" _iotdb_openssl_executable_cmake) + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + target_compile_definitions(${_t} PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") + target_link_libraries(${_t} PRIVATE iotdb_thrift_static) + endif() + endforeach() +endif() + # Optional: stage a self-contained folder for copying to another machine (see package README). set(_example_dist_dir "${CMAKE_BINARY_DIR}/dist") add_custom_target(example-dist DEPENDS ${_example_targets} @@ -194,29 +309,27 @@ if(EXISTS "${_iotdb_runtime}") COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_iotdb_runtime}" "${_example_dist_dir}/") endif() -# Stage the bundled OpenSSL runtime too, so a copied dist/ runs on a machine -# without a system OpenSSL. -foreach(_ssl_lib IN LISTS _iotdb_sdk_ssl_runtime) +# Stage the bundled SSL runtime too, so a copied dist/ runs without a system SSL. +foreach(_ssl_lib IN LISTS _iotdb_bundled_ssl_runtime) add_custom_command(TARGET example-dist POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_ssl_lib}" "${_example_dist_dir}/") endforeach() if(IOTDB_EXAMPLES_REGISTER_TESTS) - set(_runnable_example_targets - SessionExample - AlignedTimeseriesSessionExample - TableModelSessionExample - tree_example - table_example) - foreach(_t IN LISTS _runnable_example_targets) + foreach(_t IN LISTS _it_plain_examples) + add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "plain" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) + endforeach() + foreach(_t IN LISTS _it_ssl_examples) + add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "ssl" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) + endforeach() + foreach(_t IN LISTS _it_ntls_examples) add_test(NAME example_${_t} COMMAND ${_t}) + set_tests_properties(example_${_t} PROPERTIES + LABELS "ntls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) endforeach() - set_tests_properties( - example_SessionExample - example_AlignedTimeseriesSessionExample - example_TableModelSessionExample - example_tree_example - example_table_example - PROPERTIES RUN_SERIAL TRUE) endif() diff --git a/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp new file mode 100644 index 000000000000..071685ce02bb --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.cpp @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ExampleNtlsHandshake.h" + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +extern "C" int example_run_tlcp_handshake(void) { +#if !WITH_SSL + return 1; +#else + const std::string caFile = ssltest::tlcpFixture("ca.crt"); + const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); + const std::string signKey = ssltest::tlcpFixture("server_sign.key"); + const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); + const std::string encKey = ssltest::tlcpFixture("server_enc.key"); + if (!fixtureExists(caFile) || !fixtureExists(signCert) || !fixtureExists(signKey) || + !fixtureExists(encCert) || !fixtureExists(encKey)) { + return 1; + } + + ssltest::OpenSslServerProcess server; + if (!server.start({ + "-enable_ntls", + "-ntls", + "-CAfile", + caFile, + "-sign_cert", + signCert, + "-sign_key", + signKey, + "-enc_cert", + encCert, + "-enc_key", + encKey, + "-www", + })) { + return 1; + } + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + + const bool ok = ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port()); + server.stop(); + return ok ? 0 : 1; +#endif +} diff --git a/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h new file mode 100644 index 000000000000..69ab77ab9267 --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleNtlsHandshake.h @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_EXAMPLE_NTLS_HANDSHAKE_H +#define IOTDB_EXAMPLE_NTLS_HANDSHAKE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Run TLCP one-way handshake against a local openssl NTLS s_server. Returns 0 on success. */ +int example_run_tlcp_handshake(void); + +#ifdef __cplusplus +} +#endif + +#endif // IOTDB_EXAMPLE_NTLS_HANDSHAKE_H diff --git a/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp b/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp new file mode 100644 index 000000000000..7204174a3923 --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleTlsConfig.cpp @@ -0,0 +1,136 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "Session.h" +#include "TableSession.h" +#include "ExampleTlsConfig.h" + +#include +#include + +namespace { + +constexpr const char* kStorePassword = "thrift"; + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +#if defined(_WIN32) +#include +#else +#include +#endif + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string trustStorePath() { + static const std::string path = [] { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + const std::string configuredPath = joinPath(joinPath(configured, "tls"), "tls-trust.p12"); + if (pathExists(configuredPath)) { + return configuredPath; + } +#endif + const std::string copied = joinPath(joinPath(executableDir(), "fixtures"), "tls/tls-trust.p12"); + return copied; + }(); + return path; +} + +} // namespace + +extern "C" const char* example_tls_trust_store_path(void) { + static std::string path = trustStorePath(); + return path.c_str(); +} + +extern "C" void example_tls_configure_tree_session(CSession* session) { + if (session == nullptr) { + return; + } + ts_session_set_use_ssl(session, true); + ts_session_set_ssl_protocol(session, "TLS"); + ts_session_set_trust_store(session, example_tls_trust_store_path(), kStorePassword); +} + +extern "C" void example_tls_configure_table_session(CTableSession* session) { + if (session == nullptr) { + return; + } + ts_table_session_set_use_ssl(session, true); + ts_table_session_set_ssl_protocol(session, "TLS"); + ts_table_session_set_trust_store(session, example_tls_trust_store_path(), kStorePassword); +} + +namespace examplessl { + +void configureTreeSessionBuilder(SessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(trustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureTableSessionBuilder(TableSessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(trustStorePath()) + ->trustStorePwd(kStorePassword); +} + +} // namespace examplessl diff --git a/iotdb-client/client-cpp/examples/ExampleTlsConfig.h b/iotdb-client/client-cpp/examples/ExampleTlsConfig.h new file mode 100644 index 000000000000..8473d218bcaa --- /dev/null +++ b/iotdb-client/client-cpp/examples/ExampleTlsConfig.h @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_EXAMPLE_TLS_CONFIG_H +#define IOTDB_EXAMPLE_TLS_CONFIG_H + +#include "SessionC.h" + +#ifdef __cplusplus +#include "Session.h" +#include "SessionBuilder.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** One-way TLS trust store used by TLS examples (PKCS12 under fixtures/tls/). */ +const char* example_tls_trust_store_path(void); + +void example_tls_configure_tree_session(CSession* session); +void example_tls_configure_table_session(CTableSession* session); + +#ifdef __cplusplus +} + +namespace examplessl { + +void configureTreeSessionBuilder(SessionBuilder& builder); +void configureTableSessionBuilder(TableSessionBuilder& builder); + +} // namespace examplessl +#endif + +#endif // IOTDB_EXAMPLE_TLS_CONFIG_H diff --git a/iotdb-client/client-cpp/examples/README.md b/iotdb-client/client-cpp/examples/README.md index 763ec693bee2..dbace0eba276 100644 --- a/iotdb-client/client-cpp/examples/README.md +++ b/iotdb-client/client-cpp/examples/README.md @@ -32,12 +32,18 @@ user `root` / `root`). | Example | Description | |---------|-------------| -| `SessionExample` | Tree model: DDL, insert, query, delete | -| `AlignedTimeseriesSessionExample` | Aligned time series and templates | -| `TableModelSessionExample` | Table (relational) model | +| `cpp_tree_example` | C++ tree model smoke test (plain RPC) | +| `cpp_table_example` | C++ table model smoke test (plain RPC) | +| `cpp_tls_example` | C++ tree model over one-way TLS | +| `cpp_ntls_example` | C++ TLCP handshake against local openssl NTLS `s_server` | +| `tree_example` | C Session API tree model (plain RPC) | +| `table_example` | C Session API table model (plain RPC) | +| `tls_tree_example` | C Session API tree model over one-way TLS | +| `c_ntls_example` | C TLCP handshake against local openssl NTLS `s_server` | +| `SessionExample` | Full tree-model walkthrough (not run in CI) | +| `AlignedTimeseriesSessionExample` | Aligned time series demo (not run in CI) | +| `TableModelSessionExample` | Full table-model walkthrough (not run in CI) | | `MultiSvrNodeClient` | Multi-node insert/query loop | -| `tree_example` | C Session API (tree model) | -| `table_example` | C Session API (table model) | ## Which SDK zip to use @@ -61,8 +67,9 @@ pre-built Thrift workflow only. Linux release packages are built in the ## SDK layout (after unpack) -The SDK zip produced by `client-cpp` contains **public headers only** and one -shared library: +The SDK zip produced by `client-cpp` contains **public headers**, the +`iotdb_session` shared library, and (when built with SSL, the default) +**bundled Tongsuo** runtime libraries (`libssl` / `libcrypto`): ``` client/ @@ -73,7 +80,9 @@ client/ └── lib/ ├── iotdb_session.dll + iotdb_session.lib (Windows) ├── libiotdb_session.so (Linux) - └── libiotdb_session.dylib (macOS) + ├── libiotdb_session.dylib (macOS) + ├── libssl-3-x64.dll + libcrypto-3-x64.dll (Windows SSL runtime, when WITH_SSL=ON) + └── libssl.so* + libcrypto.so* (Linux/macOS SSL runtime, when WITH_SSL=ON) ``` ## Build the examples @@ -106,6 +115,10 @@ cmake -S iotdb-client/client-cpp/examples -B build \ cmake --build build ``` +When the SDK bundles `libssl` / `libcrypto` under `lib/` (default `WITH_SSL=ON` +builds), CMake detects them automatically. A system OpenSSL install is not used. +Pass `-DWITH_SSL=OFF` only for SDKs built without SSL. + Windows (Visual Studio generator): ```powershell @@ -122,6 +135,7 @@ Optional staging folder for deployment: ```bash cmake --build build --target example-dist # -> build/dist/ contains all example binaries + libiotdb_session.{so,dll,dylib} +# and bundled libssl/libcrypto when WITH_SSL=ON ``` ## Run on a clean machine (no compiler, no IoTDB SDK headers) @@ -142,9 +156,12 @@ Copy either from `build/.../Release/` (Windows) / `build/` (Ninja/Make) or from ``` SessionExample.exe iotdb_session.dll +libssl-3-x64.dll +libcrypto-3-x64.dll ``` -(Repeat for the other example names if needed.) +(Repeat for the other example names if needed. Exact SSL DLL names follow the +Tongsuo major version bundled in your SDK zip.) **Prerequisites on the target PC** @@ -164,8 +181,9 @@ iotdb_session.dll If you see “The code execution cannot proceed because VCRUNRuntime140.dll was missing”, install the VC++ redistributable above. -You do **not** need a separate Thrift or Boost runtime; they are inside -`iotdb_session.dll`. +You do **not** need a separate Thrift, Boost, or system OpenSSL runtime; Thrift +and Boost are inside `iotdb_session.dll`, and SSL is provided by the bundled +Tongsuo libraries copied above. ### Linux @@ -174,9 +192,14 @@ You do **not** need a separate Thrift or Boost runtime; they are inside ``` SessionExample libiotdb_session.so +libssl.so* +libcrypto.so* chmod +x SessionExample ``` +Copy the `libssl` / `libcrypto` soname files that ship next to +`libiotdb_session.so` in the SDK `lib/` directory (Tongsuo, OpenSSL-compatible). + **Prerequisites on the target machine** - **glibc** on the target must be **≥ the glibc version on the machine that @@ -231,6 +254,28 @@ version should be **≥ the deployment target used to build the SDK**. Check wit otool -L SessionExample ``` +## SSL / TLCP examples + +When connecting to an SSL-enabled IoTDB DataNode, configure the session builder +before `build()`: + +```cpp +SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") // or "TLCP" for NTLS / GM/T + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client.p12") // optional, mutual auth + ->keyStorePwd("thrift") + ->build(); +``` + +Use PKCS12 stores (convert JKS with `keytool -importkeystore`). See the main +[README.md](../README.md#client-ssl--tlcp-configuration) for TLS and TLCP +details. + ## Development notes - **Windows**: Application and SDK both use **`/MD`** (dynamic CRT). This diff --git a/iotdb-client/client-cpp/examples/README_zh.md b/iotdb-client/client-cpp/examples/README_zh.md index 4adc38a3fc73..233f575943d5 100644 --- a/iotdb-client/client-cpp/examples/README_zh.md +++ b/iotdb-client/client-cpp/examples/README_zh.md @@ -59,7 +59,8 @@ Linux 发版包在 `manylinux_2_28` 容器中构建,部署机需要 glibc 2.28 ## SDK 目录结构(解压后) -`client-cpp` 打出的 SDK 压缩包只包含 **公开头文件** 和 **一个共享库**: +`client-cpp` 打出的 SDK 压缩包包含 **公开头文件**、`iotdb_session` 共享库, +以及(默认开启 SSL 时)**内置的 Tongsuo** 运行时(`libssl` / `libcrypto`): ``` client/ @@ -70,7 +71,9 @@ client/ └── lib/ ├── iotdb_session.dll + iotdb_session.lib (Windows) ├── libiotdb_session.so (Linux) - └── libiotdb_session.dylib (macOS) + ├── libiotdb_session.dylib (macOS) + ├── libssl-3-x64.dll + libcrypto-3-x64.dll (Windows SSL 运行时,WITH_SSL=ON) + └── libssl.so* + libcrypto.so* (Linux/macOS SSL 运行时,WITH_SSL=ON) ``` ## 编译示例 @@ -103,6 +106,10 @@ cmake -S iotdb-client/client-cpp/examples -B build \ cmake --build build ``` +若 SDK 的 `lib/` 下已包含 `libssl` / `libcrypto`(默认 `WITH_SSL=ON` 构建), +CMake 会自动检测并链接这些内置库,不会使用系统 OpenSSL。仅当使用未启用 SSL 的 +SDK 时才需要传入 `-DWITH_SSL=OFF`。 + Windows(Visual Studio 生成器): ```powershell @@ -119,6 +126,7 @@ cmake --build build --config Release ```bash cmake --build build --target example-dist # 生成 build/dist/,内含全部示例二进制 + libiotdb_session.{so,dll,dylib} +# 以及 WITH_SSL=ON 时的 libssl/libcrypto ``` ## 在「干净机器」上运行(无需编译器、无需 SDK 头文件) @@ -139,9 +147,11 @@ cmake --build build --target example-dist ``` SessionExample.exe iotdb_session.dll +libssl-3-x64.dll +libcrypto-3-x64.dll ``` -(其他示例同理,可执行文件与 `iotdb_session.dll` 成对拷贝。) +(其他示例同理。SSL DLL 文件名与 SDK 中打包的 Tongsuo 主版本号一致。) **目标机器前置条件** @@ -160,7 +170,8 @@ iotdb_session.dll 若提示缺少 `VCRUNTIME140.dll`,请安装上述 VC++ 可再发行包。 -Thrift、Boost 已包含在 `iotdb_session.dll` 内,无需单独部署。 +Thrift、Boost 已包含在 `iotdb_session.dll` 内;SSL 由上述内置 Tongsuo 库提供, +无需单独部署系统 OpenSSL。 ### Linux @@ -169,9 +180,14 @@ Thrift、Boost 已包含在 `iotdb_session.dll` 内,无需单独部署。 ``` SessionExample libiotdb_session.so +libssl.so* +libcrypto.so* chmod +x SessionExample ``` +请一并拷贝 SDK `lib/` 目录中与 `libiotdb_session.so` 同目录的 `libssl` / +`libcrypto` 文件(Tongsuo,OpenSSL 兼容)。 + **目标机器前置条件** - 目标机的 **glibc 版本必须 ≥ 编译 SDK 时的 glibc 版本**(仅向后兼容: @@ -226,6 +242,26 @@ export LD_LIBRARY_PATH=. otool -L SessionExample ``` +## SSL / TLCP 示例 + +连接已启用 SSL 的 DataNode 时,在 `build()` 前配置: + +```cpp +SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLS") // 国密请使用 "TLCP" + ->trustStore("/path/to/ca.p12") + ->trustStorePwd("thrift") + ->keyStore("/path/to/client.p12") // 可选,双向认证 + ->keyStorePwd("thrift") + ->build(); +``` + +请使用 PKCS12 证书库(JKS 可用 `keytool -importkeystore` 转换)。详见 +[README.md](../README.md#client-ssl--tlcp-configuration)。 + ## 开发说明 - **Windows**:应用与 SDK 均使用 **`/MD`**,与 Visual Studio 默认工程一致; diff --git a/iotdb-client/client-cpp/examples/SessionExample.cpp b/iotdb-client/client-cpp/examples/SessionExample.cpp index 9b429b0189b3..1546204359e0 100644 --- a/iotdb-client/client-cpp/examples/SessionExample.cpp +++ b/iotdb-client/client-cpp/examples/SessionExample.cpp @@ -452,6 +452,7 @@ int main() { session->close(); delete session; + session = nullptr; cout << "finished!\n" << endl; return 0; diff --git a/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp b/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp index 3ae321e18f88..ef1195b167c1 100644 --- a/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp +++ b/iotdb-client/client-cpp/examples/TableModelSessionExample.cpp @@ -24,6 +24,10 @@ using namespace std; shared_ptr session; +static void configureTableBuilder(TableSessionBuilder& builder) { + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); +} + void insertRelationalTablet() { vector> schemaList{ @@ -86,12 +90,9 @@ void OutputWithType(unique_ptr& dataSet) { int main() { try { - session = (new TableSessionBuilder()) - ->host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->build(); + TableSessionBuilder builder; + configureTableBuilder(builder); + session = builder.build(); cout << "[Create Database db1,db2]\n" << endl; try { @@ -156,14 +157,10 @@ int main() { session->close(); - // specify database in constructor - session = (new TableSessionBuilder()) - ->host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->database("db1") - ->build(); + TableSessionBuilder builder2; + configureTableBuilder(builder2); + builder2.database("db1"); + session = builder2.build(); cout << "[Show tables from current database(db1)]\n" << endl; try { diff --git a/iotdb-client/client-cpp/examples/c_ntls_example.c b/iotdb-client/client-cpp/examples/c_ntls_example.c new file mode 100644 index 000000000000..b131a1009231 --- /dev/null +++ b/iotdb-client/client-cpp/examples/c_ntls_example.c @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "ExampleNtlsHandshake.h" + +int main(void) { + if (example_run_tlcp_handshake() != 0) { + fprintf(stderr, "[c_ntls_example] TLCP handshake failed\n"); + return 1; + } + printf("[c_ntls_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp b/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp new file mode 100644 index 000000000000..8a0cd37f1f0d --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_ntls_example.cpp @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "ExampleNtlsHandshake.h" + +int main(void) { + if (example_run_tlcp_handshake() != 0) { + fprintf(stderr, "[cpp_ntls_example] TLCP handshake failed\n"); + return 1; + } + printf("[cpp_ntls_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_table_example.cpp b/iotdb-client/client-cpp/examples/cpp_table_example.cpp new file mode 100644 index 000000000000..f38fa9d46abd --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_table_example.cpp @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "SessionDataSet.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" + +int main() { + TableSessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + std::shared_ptr session = builder.build(); + session->open(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_demo_table"); + session->executeNonQueryStatement("CREATE DATABASE cpp_demo_table"); + session->executeNonQueryStatement("USE cpp_demo_table"); + session->executeNonQueryStatement( + "CREATE TABLE IF NOT EXISTS demo_t (tag1 STRING TAG, value INT32 FIELD)"); + session->executeNonQueryStatement("INSERT INTO demo_t(time, tag1, value) VALUES (1, 'a', 42)"); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT time, value FROM demo_t WHERE tag1 = 'a'")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_table_example] expected one row\n"; + return 1; + } + std::shared_ptr record = dataSet->next(); + if (record->fields[1].intV.value() != 42) { + std::cerr << "[cpp_table_example] unexpected value\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_demo_table"); + session->close(); + std::cout << "[cpp_table_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_tls_example.cpp b/iotdb-client/client-cpp/examples/cpp_tls_example.cpp new file mode 100644 index 000000000000..6e20f098ebc4 --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_tls_example.cpp @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "ExampleTlsConfig.h" +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionDataSet.h" + +int main() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + examplessl::configureTreeSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(false); + + const std::string database = "root.cpp_demo_tls"; + const std::string timeseries = database + ".d0.s0"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, + CompressionType::UNCOMPRESSED); + session->insertRecord(database + ".d0", 1, {"s0"}, {"7"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s0 FROM " + database + ".d0")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_tls_example] expected one row\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); + std::cout << "[cpp_tls_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/cpp_tree_example.cpp b/iotdb-client/client-cpp/examples/cpp_tree_example.cpp new file mode 100644 index 000000000000..8043ae2eeefc --- /dev/null +++ b/iotdb-client/client-cpp/examples/cpp_tree_example.cpp @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionDataSet.h" + +int main() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + std::shared_ptr session = builder.build(); + session->open(false); + + const std::string database = "root.cpp_demo_tree"; + const std::string timeseries = database + ".d0.s0"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT64, TSEncoding::RLE, + CompressionType::SNAPPY); + session->insertRecord(database + ".d0", 1, {"s0"}, {"100"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s0 FROM " + database + ".d0")); + if (!dataSet || !dataSet->hasNext()) { + std::cerr << "[cpp_tree_example] expected one row\n"; + return 1; + } + std::shared_ptr record = dataSet->next(); + if (record->fields[0].longV.value() != 100) { + std::cerr << "[cpp_tree_example] unexpected value\n"; + return 1; + } + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); + std::cout << "[cpp_tree_example] ok\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/examples/tls_tree_example.c b/iotdb-client/client-cpp/examples/tls_tree_example.c new file mode 100644 index 000000000000..edd44a250799 --- /dev/null +++ b/iotdb-client/client-cpp/examples/tls_tree_example.c @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include "ExampleTlsConfig.h" +#include "SessionC.h" + +#define HOST "127.0.0.1" +#define PORT 6667 +#define USER "root" +#define PASS "root" +#define TS_PATH "root.cdemo_tls.d0.s0" +#define DEVICE "root.cdemo_tls.d0" + +static void fail(const char* ctx, CSession* s) { + fprintf(stderr, "[tls_tree_example] %s failed: %s\n", ctx, ts_get_last_error()); + if (s) { + ts_session_close(s); + ts_session_destroy(s); + } + exit(1); +} + +int main(void) { + CSession* session = ts_session_new(HOST, PORT, USER, PASS); + if (!session) { + fprintf(stderr, "[tls_tree_example] ts_session_new returned NULL\n"); + return 1; + } + example_tls_configure_tree_session(session); + if (ts_session_open(session) != TS_OK) { + fail("ts_session_open", session); + } + + bool exists = false; + if (ts_session_check_timeseries_exists(session, TS_PATH, &exists) != TS_OK) { + fail("ts_session_check_timeseries_exists", session); + } + if (exists) { + (void)ts_session_delete_timeseries(session, TS_PATH); + } + if (ts_session_create_timeseries(session, TS_PATH, TS_TYPE_INT64, TS_ENCODING_RLE, + TS_COMPRESSION_SNAPPY) != TS_OK) { + fail("ts_session_create_timeseries", session); + } + + const char* measurements[] = {"s0"}; + const char* values[] = {"100"}; + if (ts_session_insert_record_str(session, DEVICE, 1LL, 1, measurements, values) != TS_OK) { + fail("ts_session_insert_record_str", session); + } + + CSessionDataSet* dataSet = NULL; + if (ts_session_execute_query(session, "select s0 from root.cdemo_tls.d0", &dataSet) != TS_OK) { + fail("ts_session_execute_query", session); + } + if (!dataSet || !ts_dataset_has_next(dataSet)) { + fail("ts_session_execute_query empty", session); + } + ts_dataset_destroy(dataSet); + + (void)ts_session_delete_timeseries(session, TS_PATH); + ts_session_close(session); + ts_session_destroy(session); + printf("[tls_tree_example] ok\n"); + return 0; +} diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index 04f7fa1bd2db..f0e9fcd116f6 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -38,7 +38,7 @@ 3. Packages the produced install tree (maven-assembly-plugin) Everything else - thrift download, code generation, Boost/m4/flex/bison - bootstrap, OpenSSL discovery - lives in CMake modules under cmake/. + bootstrap, Tongsuo build - lives in CMake modules under cmake/. --> https://github.com/catchorg/Catch2/releases/download/v2.13.7/catch.hpp @@ -50,7 +50,9 @@ ${project.basedir}/third-party OFF ON - OFF + 6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242 + 8.4.0 + 57c2741750a699bfbdaa1bbe44a5733e9c8fc65d086c210151cfbc2bbd6fc975 ON @@ -63,6 +65,7 @@ ${os.classifier} iotdb-session-cpp-${project.version}-${client.cpp.package.classifier} ${env.GITHUB_RUN_ID} + ${project.basedir}/../../distribution/target/apache-iotdb-${project.version}-all-bin/apache-iotdb-${project.version}-all-bin @@ -113,7 +116,9 @@ - + + + @@ -139,16 +144,56 @@ + cmake-run-test test + none + + + + + org.codehaus.mojo + exec-maven-plugin + + + configure-iotdb-plain-it + + exec + + pre-integration-test + + ${ctest.skip.tests} + python + + ${project.basedir}/test/scripts/configure_iotdb_ssl_it.py + ${iotdb.dist.root} + ${project.basedir}/test/fixtures + disable + + + + + run-cpp-it-phases + + exec + integration-test - ${cmake.build.type} - ${cmake.project.dir} - ${maven.test.skip} + ${ctest.skip.tests} + python + + ${project.basedir}/test/scripts/run_cpp_it_phases.py + ${cmake.project.dir} + ${iotdb.dist.root} + ${project.basedir}/test/fixtures + ${project.basedir}/test/scripts + ${iotdb.start.script} + --config + ${cmake.build.type} + diff --git a/iotdb-client/client-cpp/src/assembly/client-cpp.xml b/iotdb-client/client-cpp/src/assembly/client-cpp.xml index 3a6a63136410..c791da956962 100644 --- a/iotdb-client/client-cpp/src/assembly/client-cpp.xml +++ b/iotdb-client/client-cpp/src/assembly/client-cpp.xml @@ -97,8 +97,22 @@ ${project.basedir}/examples CMakeLists.txt - *.c - *.cpp + README.md + README_zh.md + SessionExample.cpp + AlignedTimeseriesSessionExample.cpp + TableModelSessionExample.cpp + MultiSvrNodeClient.cpp + tree_example.c + table_example.c + cpp_tls_example.cpp + cpp_ntls_example.cpp + tls_tree_example.c + c_ntls_example.c + ExampleTlsConfig.h + ExampleTlsConfig.cpp + ExampleNtlsHandshake.h + ExampleNtlsHandshake.cpp examples diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md index e321c6fe9847..ee3a87e88919 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md @@ -31,9 +31,9 @@ the [`NOTICE`](NOTICE) file in this directory; non-Apache license texts are unde | Component | Version | How | License | | --- | --- | --- | --- | -| Apache Thrift | 0.23.0 | statically linked | Apache License 2.0 | +| Apache Thrift | 6dfb0b26 (post-0.23.0) | statically linked | Apache License 2.0 | | Boost | 1.60.0 on Linux/Windows, 1.84.0 on macOS by default | statically linked (header-only) | Boost Software License 1.0 | -| OpenSSL | 3.x: system OpenSSL 3.x when present, else 3.5.0 built from source (`WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 | +| Tongsuo | 8.4.0 (always built from source when `WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 | ## Build-time only (not redistributed) diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE index 4da431faa062..39bb234d1b44 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE @@ -19,10 +19,12 @@ This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ------------------------------------------------------------------------------ -OpenSSL (bundled shared libraries: libssl / libcrypto, present only when the +Tongsuo (bundled shared libraries: libssl / libcrypto, present only when the SDK is built with SSL support) -Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. +Copyright The Tongsuo Project Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (see the top-level LICENSE). +Tongsuo is an OpenSSL-compatible cryptographic library with additional Chinese +commercial cipher and TLCP protocol support. ------------------------------------------------------------------------------ Boost C++ Libraries (header-only; used at build time to compile Apache Thrift diff --git a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h index 3735dfa227d0..6217a2e73761 100644 --- a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h @@ -55,7 +55,13 @@ class AbstractSessionBuilder { bool enableRPCCompression = DEFAULT_ENABLE_RPC_COMPRESSION; std::vector nodeUrls; bool useSSL = false; + /** @deprecated Use trustStore() instead. Legacy PEM trust certificate path. */ std::string trustCertFilePath; + std::string sslProtocol = "TLS"; + std::string trustStore; + std::string trustStorePwd; + std::string keyStore; + std::string keyStorePwd; }; #endif // IOTDB_ABSTRACTSESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/Session.h b/iotdb-client/client-cpp/src/include/Session.h index a0910584e577..963725589a5d 100644 --- a/iotdb-client/client-cpp/src/include/Session.h +++ b/iotdb-client/client-cpp/src/include/Session.h @@ -19,6 +19,8 @@ #ifndef IOTDB_SESSION_H #define IOTDB_SESSION_H +struct SslConfig; + #include #include #include @@ -600,6 +602,7 @@ class Session { void setSqlDialect(const std::string& dialect); void setDatabase(const std::string& database); + void setSslConfig(const SslConfig& sslConfig); std::string getDatabase(); void changeDatabase(const std::string& database); diff --git a/iotdb-client/client-cpp/src/include/SessionBuilder.h b/iotdb-client/client-cpp/src/include/SessionBuilder.h index 14342697eb5d..5d3eabd7434e 100644 --- a/iotdb-client/client-cpp/src/include/SessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/SessionBuilder.h @@ -44,6 +44,31 @@ class SessionBuilder : public AbstractSessionBuilder { return this; } + SessionBuilder* sslProtocol(const std::string& sslProtocol) { + AbstractSessionBuilder::sslProtocol = sslProtocol; + return this; + } + + SessionBuilder* trustStore(const std::string& trustStore) { + AbstractSessionBuilder::trustStore = trustStore; + return this; + } + + SessionBuilder* trustStorePwd(const std::string& trustStorePwd) { + AbstractSessionBuilder::trustStorePwd = trustStorePwd; + return this; + } + + SessionBuilder* keyStore(const std::string& keyStore) { + AbstractSessionBuilder::keyStore = keyStore; + return this; + } + + SessionBuilder* keyStorePwd(const std::string& keyStorePwd) { + AbstractSessionBuilder::keyStorePwd = keyStorePwd; + return this; + } + SessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/include/SessionC.h b/iotdb-client/client-cpp/src/include/SessionC.h index fdce5801a9de..bad2b63710b8 100644 --- a/iotdb-client/client-cpp/src/include/SessionC.h +++ b/iotdb-client/client-cpp/src/include/SessionC.h @@ -131,6 +131,14 @@ TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompr TsStatus ts_session_close(CSession* session); +TsStatus ts_session_set_use_ssl(CSession* session, bool useSsl); +TsStatus ts_session_set_ssl_protocol(CSession* session, const char* sslProtocol); +TsStatus ts_session_set_trust_store(CSession* session, const char* trustStore, + const char* trustStorePwd); +TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, const char* keyStorePwd); +/** @deprecated Use ts_session_set_trust_store() instead. */ +TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath); + /* ============================================================ * Session Lifecycle — Table Model * ============================================================ */ @@ -148,6 +156,16 @@ TsStatus ts_table_session_open(CTableSession* session); TsStatus ts_table_session_close(CTableSession* session); +TsStatus ts_table_session_set_use_ssl(CTableSession* session, bool useSsl); +TsStatus ts_table_session_set_ssl_protocol(CTableSession* session, const char* sslProtocol); +TsStatus ts_table_session_set_trust_store(CTableSession* session, const char* trustStore, + const char* trustStorePwd); +TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyStore, + const char* keyStorePwd); +/** @deprecated Use ts_table_session_set_trust_store() instead. */ +TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, + const char* trustCertFilePath); + /* ============================================================ * Timezone * ============================================================ */ diff --git a/iotdb-client/client-cpp/src/include/SessionPool.h b/iotdb-client/client-cpp/src/include/SessionPool.h index 4483dab0c514..c71580262446 100644 --- a/iotdb-client/client-cpp/src/include/SessionPool.h +++ b/iotdb-client/client-cpp/src/include/SessionPool.h @@ -188,6 +188,11 @@ class SessionPool { SessionPool& setWaitToGetSessionTimeoutMs(int64_t timeoutMs); SessionPool& setUseSSL(bool useSSL); SessionPool& setTrustCertFilePath(std::string path); + SessionPool& setSslProtocol(std::string sslProtocol); + SessionPool& setTrustStore(std::string trustStore); + SessionPool& setTrustStorePwd(std::string trustStorePwd); + SessionPool& setKeyStore(std::string keyStore); + SessionPool& setKeyStorePwd(std::string keyStorePwd); // Borrow a Session. Blocks until one is free or a new one can be created, // up to timeoutMs (<= 0 means use the pool default). Throws IoTDBException on @@ -249,6 +254,11 @@ class SessionPool { int connectTimeoutMs_ = AbstractSessionBuilder::DEFAULT_CONNECT_TIMEOUT_MS; bool useSSL_ = false; std::string trustCertFilePath_; + std::string sslProtocol_ = "TLS"; + std::string trustStore_; + std::string trustStorePwd_; + std::string keyStore_; + std::string keyStorePwd_; // pool sizing / waiting policy size_t maxSize_; @@ -339,6 +349,26 @@ class SessionPoolBuilder : public AbstractSessionBuilder { AbstractSessionBuilder::trustCertFilePath = v; return this; } + SessionPoolBuilder* sslProtocol(const std::string& v) { + AbstractSessionBuilder::sslProtocol = v; + return this; + } + SessionPoolBuilder* trustStore(const std::string& v) { + AbstractSessionBuilder::trustStore = v; + return this; + } + SessionPoolBuilder* trustStorePwd(const std::string& v) { + AbstractSessionBuilder::trustStorePwd = v; + return this; + } + SessionPoolBuilder* keyStore(const std::string& v) { + AbstractSessionBuilder::keyStore = v; + return this; + } + SessionPoolBuilder* keyStorePwd(const std::string& v) { + AbstractSessionBuilder::keyStorePwd = v; + return this; + } SessionPoolBuilder* maxSize(size_t v) { maxSize_ = v; return this; @@ -380,7 +410,12 @@ class SessionPoolBuilder : public AbstractSessionBuilder { .setConnectTimeoutMs(AbstractSessionBuilder::connectTimeoutMs) .setWaitToGetSessionTimeoutMs(waitTimeoutMs_) .setUseSSL(AbstractSessionBuilder::useSSL) - .setTrustCertFilePath(AbstractSessionBuilder::trustCertFilePath); + .setTrustCertFilePath(AbstractSessionBuilder::trustCertFilePath) + .setSslProtocol(AbstractSessionBuilder::sslProtocol) + .setTrustStore(AbstractSessionBuilder::trustStore) + .setTrustStorePwd(AbstractSessionBuilder::trustStorePwd) + .setKeyStore(AbstractSessionBuilder::keyStore) + .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd); return pool; } diff --git a/iotdb-client/client-cpp/src/include/TableSession.h b/iotdb-client/client-cpp/src/include/TableSession.h index d1eecfeeabae..a57288339b6f 100644 --- a/iotdb-client/client-cpp/src/include/TableSession.h +++ b/iotdb-client/client-cpp/src/include/TableSession.h @@ -24,6 +24,8 @@ #include "Session.h" +struct SslConfig; + class TableSession { private: std::shared_ptr session_; @@ -41,6 +43,7 @@ class TableSession { unique_ptr executeQueryStatement(const std::string& sql, int64_t timeoutInMs); void open(bool enableRPCCompression = false); void close(); + void setSslConfig(const SslConfig& sslConfig); }; #endif // IOTDB_TABLESESSION_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h index 3c9739ecc8ed..0642acf0759b 100644 --- a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h @@ -55,6 +55,31 @@ class TableSessionBuilder : public AbstractSessionBuilder { return this; } + TableSessionBuilder* sslProtocol(const std::string& sslProtocol) { + AbstractSessionBuilder::sslProtocol = sslProtocol; + return this; + } + + TableSessionBuilder* trustStore(const std::string& trustStore) { + AbstractSessionBuilder::trustStore = trustStore; + return this; + } + + TableSessionBuilder* trustStorePwd(const std::string& trustStorePwd) { + AbstractSessionBuilder::trustStorePwd = trustStorePwd; + return this; + } + + TableSessionBuilder* keyStore(const std::string& keyStore) { + AbstractSessionBuilder::keyStore = keyStore; + return this; + } + + TableSessionBuilder* keyStorePwd(const std::string& keyStorePwd) { + AbstractSessionBuilder::keyStorePwd = keyStorePwd; + return this; + } + TableSessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp index 604099a82d1b..40bd764fc4cc 100644 --- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp +++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp @@ -17,6 +17,7 @@ * under the License. */ #include "NodesSupplier.h" +#include "RpcSslUtils.h" #include "Session.h" #include "SessionDataSet.h" #include @@ -66,33 +67,34 @@ std::vector StaticNodesSupplier::getEndPointList() { StaticNodesSupplier::~StaticNodesSupplier() = default; -std::shared_ptr NodesSupplier::create( - const std::vector& endpoints, const std::string& userName, - const std::string& password, bool useSSL, const std::string& trustCertFilePath, - const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, - int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, - std::chrono::milliseconds refreshInterval, NodeSelectionPolicy policy) { +std::shared_ptr +NodesSupplier::create(const std::vector& endpoints, const std::string& userName, + const std::string& password, const SslConfig& sslConfig, + const std::string& zoneId, int32_t thriftDefaultBufferSize, + int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, + bool enableRPCCompression, const std::string& version, + std::chrono::milliseconds refreshInterval, NodeSelectionPolicy policy) { if (endpoints.empty()) { return nullptr; } auto supplier = std::make_shared( - userName, password, useSSL, trustCertFilePath, zoneId, thriftDefaultBufferSize, - thriftMaxFrameSize, connectionTimeoutInMs, enableRPCCompression, version, endpoints, policy); + userName, password, sslConfig, zoneId, thriftDefaultBufferSize, thriftMaxFrameSize, + connectionTimeoutInMs, enableRPCCompression, version, endpoints, policy); supplier->startBackgroundRefresh(refreshInterval); return supplier; } -NodesSupplier::NodesSupplier(const std::string& userName, const std::string& password, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, +NodesSupplier::NodesSupplier(const std::string& userName, const std::string& password, + const SslConfig& sslConfig, const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, const std::vector& endpoints, NodeSelectionPolicy policy) : userName_(userName), password_(password), zoneId_(zoneId), thriftDefaultBufferSize_(thriftDefaultBufferSize), thriftMaxFrameSize_(thriftMaxFrameSize), - connectionTimeoutInMs_(connectionTimeoutInMs), useSSL_(useSSL), - trustCertFilePath_(trustCertFilePath), enableRPCCompression_(enableRPCCompression), - version_(version), endpoints_(endpoints), selectionPolicy_(policy) { + connectionTimeoutInMs_(connectionTimeoutInMs), sslConfig_(sslConfig), + enableRPCCompression_(enableRPCCompression), version_(version), endpoints_(endpoints), + selectionPolicy_(policy) { deduplicateEndpoints(); } @@ -155,8 +157,7 @@ std::vector NodesSupplier::fetchLatestEndpoints() { try { if (client_ == nullptr) { client_ = std::make_shared(endpoint); - client_->init(userName_, password_, enableRPCCompression_, useSSL_, trustCertFilePath_, - zoneId_, version_); + client_->init(userName_, password_, enableRPCCompression_, sslConfig_, zoneId_, version_); } auto sessionDataSet = client_->executeQueryStatement(SHOW_AVAILABLE_URLS_COMMAND); diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h index c067bbb6d722..a721ebd0751f 100644 --- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h +++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h @@ -30,6 +30,7 @@ #include #include "ThriftConnection.h" +#include "RpcSslUtils.h" class TEndPoint; @@ -78,8 +79,7 @@ class NodesSupplier : public INodesSupplier { static std::shared_ptr create(const std::vector& endpoints, const std::string& userName, - const std::string& password, bool useSSL = false, - const std::string& trustCertFilePath = "", const std::string& zoneId = "", + const std::string& password, const SslConfig& sslConfig, const std::string& zoneId = "", int32_t thriftDefaultBufferSize = ThriftConnection::THRIFT_DEFAULT_BUFFER_SIZE, int32_t thriftMaxFrameSize = ThriftConnection::THRIFT_MAX_FRAME_SIZE, int32_t connectionTimeoutInMs = ThriftConnection::CONNECTION_TIMEOUT_IN_MS, @@ -87,8 +87,8 @@ class NodesSupplier : public INodesSupplier { std::chrono::milliseconds refreshInterval = std::chrono::milliseconds(TIMEOUT_IN_MS), NodeSelectionPolicy policy = RoundRobinPolicy::select); - NodesSupplier(const std::string& userName, const std::string& password, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, + NodesSupplier(const std::string& userName, const std::string& password, + const SslConfig& sslConfig, const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize, int32_t connectionTimeoutInMs, bool enableRPCCompression, const std::string& version, const std::vector& endpoints, @@ -106,8 +106,7 @@ class NodesSupplier : public INodesSupplier { int32_t thriftDefaultBufferSize_; int32_t thriftMaxFrameSize_; int32_t connectionTimeoutInMs_; - bool useSSL_; - std::string trustCertFilePath_; + SslConfig sslConfig_; bool enableRPCCompression_; std::string version_; std::string zoneId_; diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp new file mode 100644 index 000000000000..0158a49129d8 --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -0,0 +1,695 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#if WITH_SSL +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "RpcSslUtils.h" + +#include "Common.h" + +#include +#include +#include +#include +#include + +namespace { + +std::string gDefaultProtocol = RpcSslUtils::DEFAULT_PROTOCOL; + +std::string trimToEmpty(const std::string& value) { + const auto start = value.find_first_not_of(" \t\r\n"); + if (start == std::string::npos) { + return ""; + } + const auto end = value.find_last_not_of(" \t\r\n"); + return value.substr(start, end - start + 1); +} + +bool hasText(const std::string& value) { + return !trimToEmpty(value).empty(); +} + +std::string toUpper(const std::string& value) { + std::string out = value; + std::transform(out.begin(), out.end(), out.begin(), + [](unsigned char c) { return static_cast(std::toupper(c)); }); + return out; +} + +bool endsWithIgnoreCase(const std::string& value, const std::string& suffix) { + if (value.size() < suffix.size()) { + return false; + } + const std::string tail = value.substr(value.size() - suffix.size()); + return toUpper(tail) == toUpper(suffix); +} + +bool isPkcs12Path(const std::string& path) { + return endsWithIgnoreCase(path, ".p12") || endsWithIgnoreCase(path, ".pfx"); +} + +bool isJksPath(const std::string& path) { + return endsWithIgnoreCase(path, ".jks"); +} + +void rejectJksPath(const std::string& path, const std::string& label) { + if (isJksPath(path)) { + throw IoTDBException(label + " JKS is not supported by the C++ client; " + "convert to PKCS#12 (.p12/.pfx) or use PEM"); + } +} + +#if WITH_SSL + +std::string collectOpenSslErrors() { + std::string errors; + unsigned long errCode = 0; + while ((errCode = ERR_get_error()) != 0) { + char buf[256]; + ERR_error_string_n(errCode, buf, sizeof(buf)); + if (!errors.empty()) { + errors.append("; "); + } + errors.append(buf); + } + return errors.empty() ? "unknown OpenSSL error" : errors; +} + +void throwSslError(const std::string& message) { + throw IoTDBException(message + ": " + collectOpenSslErrors()); +} + +void ensureFileReadable(const std::string& path, const std::string& label) { + if (!hasText(path)) { + throw IoTDBException(label + " path is empty"); + } + std::ifstream in(path.c_str(), std::ios::binary); + if (!in.good()) { + throw IoTDBException(label + " file not found: " + path); + } +} + +PKCS12* loadPkcs12(const std::string& path, const std::string& password) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + throwSslError("Failed to open PKCS12 file " + path); + } + PKCS12* p12 = d2i_PKCS12_bio(bio, nullptr); + BIO_free(bio); + if (p12 == nullptr) { + throwSslError("Failed to parse PKCS12 file " + path); + } + (void)password; + return p12; +} + +struct Pkcs12ParsedIdentity { + EVP_PKEY* pkey = nullptr; + X509* cert = nullptr; + STACK_OF(X509) * ca = nullptr; + + ~Pkcs12ParsedIdentity() { + if (pkey != nullptr) { + EVP_PKEY_free(pkey); + } + if (cert != nullptr) { + X509_free(cert); + } + if (ca != nullptr) { + sk_X509_pop_free(ca, X509_free); + } + } + + Pkcs12ParsedIdentity() = default; + Pkcs12ParsedIdentity(const Pkcs12ParsedIdentity&) = delete; + Pkcs12ParsedIdentity& operator=(const Pkcs12ParsedIdentity&) = delete; +}; + +void parsePkcs12OrThrow(PKCS12* p12, const std::string& password, Pkcs12ParsedIdentity& parsed, + const std::string& label) { + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) != 1) { + throwSslError("Failed to parse PKCS12 " + label); + } +} + +std::string getBagFriendlyName(PKCS12_SAFEBAG* bag) { + char* name = PKCS12_get_friendlyname(bag); + if (name == nullptr) { + return ""; + } + std::string friendlyName(name); + OPENSSL_free(name); + return friendlyName; +} + +void forEachPkcs12Bag(PKCS12* p12, const std::string& password, + const std::function& visitor) { + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(p12); + if (safes == nullptr) { + return; + } + for (int i = 0; i < sk_PKCS7_num(safes); ++i) { + PKCS7* p7 = sk_PKCS7_value(safes, i); + STACK_OF(PKCS12_SAFEBAG)* bags = nullptr; + if (PKCS7_type_is_data(p7)) { + bags = PKCS12_unpack_p7data(p7); + } else if (PKCS7_type_is_encrypted(p7)) { + bags = PKCS12_unpack_p7encdata(p7, password.c_str(), static_cast(password.size())); + } + if (bags == nullptr) { + continue; + } + for (int j = 0; j < sk_PKCS12_SAFEBAG_num(bags); ++j) { + visitor(sk_PKCS12_SAFEBAG_value(bags, j)); + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + sk_PKCS7_pop_free(safes, PKCS7_free); +} + +EVP_PKEY* extractBagPrivateKey(PKCS12_SAFEBAG* bag, const std::string& password) { + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + const PKCS8_PRIV_KEY_INFO* p8const = nullptr; + PKCS8_PRIV_KEY_INFO* p8owned = nullptr; + if (bagType == NID_pkcs8ShroudedKeyBag) { + p8owned = PKCS12_decrypt_skey(bag, password.c_str(), static_cast(password.size())); + p8const = p8owned; + } else if (bagType == NID_keyBag) { + p8const = PKCS12_SAFEBAG_get0_p8inf(bag); + } + if (p8const == nullptr) { + return nullptr; + } + EVP_PKEY* key = EVP_PKCS82PKEY(p8const); + if (p8owned != nullptr) { + PKCS8_PRIV_KEY_INFO_free(p8owned); + } + return key; +} + +bool friendlyNameContains(const std::string& friendlyName, const std::string& keyword) { + const std::string upperName = toUpper(friendlyName); + const std::string upperKeyword = toUpper(keyword); + return upperName.find(upperKeyword) != std::string::npos; +} + +void validateCertificate(X509* cert) { + if (cert == nullptr) { + return; + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (X509_cmp_current_time(X509_get0_notBefore(cert)) > 0 || + X509_cmp_current_time(X509_get0_notAfter(cert)) < 0) { + throw IoTDBException("Certificate is not currently valid"); + } +#else + if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0 || + X509_cmp_current_time(X509_get_notAfter(cert)) < 0) { + throw IoTDBException("Certificate is not currently valid"); + } +#endif +} + +void addCertToStore(X509_STORE* store, X509* cert) { + if (store == nullptr || cert == nullptr) { + return; + } + if (X509_STORE_add_cert(store, cert) != 1) { + const unsigned long errCode = ERR_peek_last_error(); + if (ERR_GET_LIB(errCode) != ERR_LIB_X509 || + ERR_GET_REASON(errCode) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + throwSslError("Failed to add certificate to trust store"); + } + } +} + +void loadTrustFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + Pkcs12ParsedIdentity parsed; + parsePkcs12OrThrow(p12, password, parsed, "trust store " + path); + + X509_STORE* store = SSL_CTX_get_cert_store(ctx); + if (parsed.cert != nullptr) { + validateCertificate(parsed.cert); + addCertToStore(store, parsed.cert); + } + if (parsed.ca != nullptr) { + for (int i = 0; i < sk_X509_num(parsed.ca); ++i) { + X509* caCert = sk_X509_value(parsed.ca, i); + validateCertificate(caCert); + addCertToStore(store, caCert); + } + } + + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { + X509* bagCert = PKCS12_certbag2x509(bag); + if (bagCert != nullptr) { + validateCertificate(bagCert); + addCertToStore(store, bagCert); + X509_free(bagCert); + } + } + }); + + PKCS12_free(p12); +} + +void loadTrustFromPem(SSL_CTX* ctx, const std::string& path) { + if (SSL_CTX_load_verify_locations(ctx, path.c_str(), nullptr) != 1) { + throwSslError("Failed to load PEM trust store " + path); + } +} + +void loadTrustStore(SSL_CTX* ctx, const std::string& path, const std::string& password) { + rejectJksPath(path, "Trust store"); + ensureFileReadable(path, "Trust store"); + if (isPkcs12Path(path)) { + loadTrustFromPkcs12(ctx, path, password); + } else { + loadTrustFromPem(ctx, path); + } +} + +void loadTlsIdentityFromPkcs12(SSL_CTX* ctx, const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + Pkcs12ParsedIdentity parsed; + parsePkcs12OrThrow(p12, password, parsed, "key store " + path); + PKCS12_free(p12); + + if (SSL_CTX_use_certificate(ctx, parsed.cert) != 1) { + throwSslError("Failed to load client certificate from " + path); + } + if (SSL_CTX_use_PrivateKey(ctx, parsed.pkey) != 1) { + throwSslError("Failed to load client private key from " + path); + } + if (SSL_CTX_check_private_key(ctx) != 1) { + throwSslError("Client certificate and private key do not match in " + path); + } +} + +void loadTlsIdentityFromPem(SSL_CTX* ctx, const std::string& path) { + if (SSL_CTX_use_certificate_file(ctx, path.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client certificate from " + path); + } + if (SSL_CTX_use_PrivateKey_file(ctx, path.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client private key from " + path); + } + if (SSL_CTX_check_private_key(ctx) != 1) { + throwSslError("Client certificate and private key do not match in " + path); + } +} + +void loadTlsKeyStore(SSL_CTX* ctx, const std::string& path, const std::string& password) { + rejectJksPath(path, "Key store"); + ensureFileReadable(path, "Key store"); + if (isPkcs12Path(path)) { + loadTlsIdentityFromPkcs12(ctx, path, password); + } else { + loadTlsIdentityFromPem(ctx, path); + } +} + +struct TlcpIdentity { + X509* signCert = nullptr; + EVP_PKEY* signKey = nullptr; + X509* encCert = nullptr; + EVP_PKEY* encKey = nullptr; +}; + +void freeTlcpIdentity(TlcpIdentity& identity) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + identity.signCert = nullptr; + } + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + identity.signKey = nullptr; + } + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + identity.encCert = nullptr; + } + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + identity.encKey = nullptr; + } +} + +void assignTlcpMaterial(TlcpIdentity& identity, const std::string& friendlyName, X509* cert, + EVP_PKEY* key) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + } + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + } + identity.encCert = cert; + identity.encKey = key; + return; + } + if (friendlyNameContains(friendlyName, "sign") || identity.signCert == nullptr) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + } + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + } + identity.signCert = cert; + identity.signKey = key; + return; + } + if (identity.encCert == nullptr) { + identity.encCert = cert; + identity.encKey = key; + return; + } + X509_free(cert); + EVP_PKEY_free(key); +} + +void loadTlcpKeyStoreFromPkcs12(SSL_CTX* ctx, const std::string& path, + const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + TlcpIdentity identity; + + Pkcs12ParsedIdentity parsed; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) == 1) { + assignTlcpMaterial(identity, "sign", parsed.cert, parsed.pkey); + parsed.cert = nullptr; + parsed.pkey = nullptr; + } + + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + const std::string friendlyName = getBagFriendlyName(bag); + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + if (bagType == NID_certBag) { + X509* cert = PKCS12_certbag2x509(bag); + if (cert != nullptr) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encCert != nullptr) { + X509_free(identity.encCert); + } + identity.encCert = cert; + } else if (friendlyNameContains(friendlyName, "sign") || identity.signCert == nullptr) { + if (identity.signCert != nullptr) { + X509_free(identity.signCert); + } + identity.signCert = cert; + } else if (identity.encCert == nullptr) { + identity.encCert = cert; + } else { + X509_free(cert); + } + } + } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { + EVP_PKEY* key = extractBagPrivateKey(bag, password); + if (key != nullptr) { + if (friendlyNameContains(friendlyName, "enc")) { + if (identity.encKey != nullptr) { + EVP_PKEY_free(identity.encKey); + } + identity.encKey = key; + } else if (friendlyNameContains(friendlyName, "sign") || identity.signKey == nullptr) { + if (identity.signKey != nullptr) { + EVP_PKEY_free(identity.signKey); + } + identity.signKey = key; + } else if (identity.encKey == nullptr) { + identity.encKey = key; + } else { + EVP_PKEY_free(key); + } + } + } + }); + PKCS12_free(p12); + + if (identity.signCert == nullptr || identity.signKey == nullptr) { + freeTlcpIdentity(identity); + throw IoTDBException("TLCP PKCS12 key store must contain a signing certificate and key: " + + path); + } + + if (SSL_CTX_use_sign_certificate(ctx, identity.signCert) != 1 || + SSL_CTX_use_sign_PrivateKey(ctx, identity.signKey) != 1) { + freeTlcpIdentity(identity); + throwSslError("Failed to load TLCP signing credentials from " + path); + } + + if (identity.encCert != nullptr && identity.encKey != nullptr) { + if (SSL_CTX_use_enc_certificate(ctx, identity.encCert) != 1 || + SSL_CTX_use_enc_PrivateKey(ctx, identity.encKey) != 1) { + freeTlcpIdentity(identity); + throwSslError("Failed to load TLCP encryption credentials from " + path); + } + } + + freeTlcpIdentity(identity); +} + +void applyTlsProtocolVersion(SSL_CTX* ctx, const std::string& protocol) { + const std::string resolved = RpcSslUtils::normalizeProtocol(protocol); + const std::string upper = toUpper(resolved); + if (upper == "TLSV1.2") { + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); + return; + } + if (upper == "TLSV1.3") { + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + return; + } + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); +} + +SSL_CTX* createTlsClientContext(const SslConfig& config) { + const std::string protocol = RpcSslUtils::resolveProtocol(config.sslProtocol); + SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); + if (ctx == nullptr) { + throwSslError("Failed to create TLS client context"); + } + applyTlsProtocolVersion(ctx, protocol); + + const std::string trustStore = config.effectiveTrustStore(); + if (hasText(trustStore)) { + loadTrustStore(ctx, trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + if (hasText(config.keyStore)) { + loadTlsKeyStore(ctx, config.keyStore, config.keyStorePwd); + } + return ctx; +} + +SSL_CTX* createTlcpClientContext(const SslConfig& config) { + SSL_CTX* ctx = SSL_CTX_new(NTLS_client_method()); + if (ctx == nullptr) { + throwSslError("Failed to create TLCP client context"); + } + SSL_CTX_enable_ntls(ctx); + if (SSL_CTX_set_cipher_list(ctx, RpcSslUtils::DEFAULT_TLCP_CIPHER) != 1) { + SSL_CTX_free(ctx); + throwSslError("Failed to set TLCP cipher suite"); + } + + const std::string trustStore = config.effectiveTrustStore(); + if (hasText(trustStore)) { + loadTrustStore(ctx, trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + if (hasText(config.keyStore)) { + loadTlcpKeyStoreFromPkcs12(ctx, config.keyStore, config.keyStorePwd); + } + return ctx; +} + +void validatePkcs12Store(const std::string& path, const std::string& password) { + PKCS12* p12 = loadPkcs12(path, password); + Pkcs12ParsedIdentity parsed; + if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, + &parsed.ca) != 1) { + PKCS12_free(p12); + throw IoTDBException("Failed to parse PKCS12 store: " + path); + } + if (parsed.cert != nullptr) { + validateCertificate(parsed.cert); + } + if (parsed.ca != nullptr) { + for (int i = 0; i < sk_X509_num(parsed.ca); ++i) { + validateCertificate(sk_X509_value(parsed.ca, i)); + } + } + + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { + X509* bagCert = PKCS12_certbag2x509(bag); + if (bagCert != nullptr) { + validateCertificate(bagCert); + X509_free(bagCert); + } + } + }); + PKCS12_free(p12); +} + +void validatePemStore(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + throw IoTDBException("Store file not found: " + path); + } + bool foundCert = false; + while (true) { + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (cert == nullptr) { + break; + } + validateCertificate(cert); + X509_free(cert); + foundCert = true; + } + BIO_free(bio); + if (!foundCert) { + throw IoTDBException("No valid certificate found in PEM store: " + path); + } +} + +#endif // WITH_SSL + +} // namespace + +std::string SslConfig::effectiveTrustStore() const { + if (hasText(trustStore)) { + return trimToEmpty(trustStore); + } + return trimToEmpty(trustCertFilePath); +} + +void RpcSslUtils::configure(const std::string& sslProtocol) { + gDefaultProtocol = normalizeProtocol(sslProtocol); +} + +std::string RpcSslUtils::getProtocol() { + return gDefaultProtocol; +} + +bool RpcSslUtils::isTlcpProtocol(const std::string& protocol) { + return toUpper(trimToEmpty(protocol)).find("TLCP") == 0; +} + +std::string RpcSslUtils::normalizeProtocol(const std::string& value) { + const std::string trimmed = trimToEmpty(value); + return trimmed.empty() ? DEFAULT_PROTOCOL : trimmed; +} + +std::string RpcSslUtils::resolveProtocol(const std::string& value) { + const std::string trimmed = trimToEmpty(value); + return trimmed.empty() ? gDefaultProtocol : trimmed; +} + +void RpcSslUtils::validateTrustStore(const std::string& trustStorePath, + const std::string& trustStorePassword) { +#if WITH_SSL + rejectJksPath(trustStorePath, "Trust store"); + ensureFileReadable(trustStorePath, "Trust store"); + if (isPkcs12Path(trustStorePath)) { + validatePkcs12Store(trustStorePath, trustStorePassword); + } else { + validatePemStore(trustStorePath); + } +#else + (void)trustStorePath; + (void)trustStorePassword; + throw IoTDBException("SSL/TLS support is not enabled in this build."); +#endif +} + +void RpcSslUtils::validateKeyStore(const std::string& keyStorePath, + const std::string& keyStorePassword) { +#if WITH_SSL + rejectJksPath(keyStorePath, "Key store"); + ensureFileReadable(keyStorePath, "Key store"); + if (isPkcs12Path(keyStorePath)) { + validatePkcs12Store(keyStorePath, keyStorePassword); + } else { + validatePemStore(keyStorePath); + } +#else + (void)keyStorePath; + (void)keyStorePassword; + throw IoTDBException("SSL/TLS support is not enabled in this build."); +#endif +} + +#if WITH_SSL + +void RpcSslUtils::enableNtlsOnSsl(SSL* ssl) { + if (ssl != nullptr) { + SSL_enable_ntls(ssl); + } +} + +SSL_CTX* RpcSslUtils::createClientSslContext(const SslConfig& config) { + const std::string protocol = resolveProtocol(config.sslProtocol); + if (isTlcpProtocol(protocol)) { + return createTlcpClientContext(config); + } + return createTlsClientContext(config); +} + +std::shared_ptr +RpcSslUtils::createSslSocketFactory(const SslConfig& config) { + auto sslConfig = std::make_shared(config); + auto factory = std::make_shared( + [sslConfig]() -> std::shared_ptr { + SSL_CTX* ctx = createClientSslContext(*sslConfig); + return std::make_shared(ctx); + }); + factory->authenticate(!config.effectiveTrustStore().empty()); + return factory; +} + +#endif diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h new file mode 100644 index 000000000000..1b717f714d3a --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h @@ -0,0 +1,77 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_RPC_SSL_UTILS_H +#define IOTDB_RPC_SSL_UTILS_H + +#include +#include + +#if WITH_SSL +#include +#endif + +namespace apache { +namespace thrift { +namespace transport { +class TSSLSocketFactory; +} // namespace transport +} // namespace thrift +} // namespace apache + +struct SslConfig { + bool useSsl = false; + std::string sslProtocol = "TLS"; + std::string trustStore; + std::string trustStorePwd; + std::string keyStore; + std::string keyStorePwd; + /** Legacy PEM trust certificate path; used when trustStore is empty. */ + std::string trustCertFilePath; + + std::string effectiveTrustStore() const; +}; + +class RpcSslUtils { +public: + static constexpr const char* DEFAULT_PROTOCOL = "TLS"; + static constexpr const char* DEFAULT_TLCP_CIPHER = "ECC-SM2-WITH-SM4-SM3"; + + static void configure(const std::string& sslProtocol); + static std::string getProtocol(); + + static bool isTlcpProtocol(const std::string& protocol); + static std::string normalizeProtocol(const std::string& value); + static std::string resolveProtocol(const std::string& value); + + static void validateTrustStore(const std::string& trustStorePath, + const std::string& trustStorePassword); + static void validateKeyStore(const std::string& keyStorePath, + const std::string& keyStorePassword); + +#if WITH_SSL + static void enableNtlsOnSsl(SSL* ssl); + + static SSL_CTX* createClientSslContext(const SslConfig& config); + static std::shared_ptr + createSslSocketFactory(const SslConfig& config); +#endif +}; + +#endif // IOTDB_RPC_SSL_UTILS_H diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp index dfdb0198e387..d9a78b06c1ad 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp @@ -18,6 +18,7 @@ */ #include "SessionConnection.h" #include "SessionImpl.h" +#include "RpcSslUtils.h" #include "RpcCommon.h" #include "common_types.h" #include @@ -46,7 +47,7 @@ SessionConnection::SessionConnection(Session::Impl* session_ptr, const TEndPoint sqlDialect(std::move(dialect)), database(std::move(db)) { this->zoneId = zoneId.empty() ? getSystemDefaultZoneId() : zoneId; endPointList.push_back(endpoint); - init(endPoint, session->useSSL_, session->trustCertFilePath_); + init(endPoint, session->sslConfig_); } void SessionConnection::close() { @@ -92,12 +93,10 @@ SessionConnection::~SessionConnection() { } } -void SessionConnection::init(const TEndPoint& endpoint, bool useSSL, - const std::string& trustCertFilePath) { - if (useSSL) { +void SessionConnection::init(const TEndPoint& endpoint, const SslConfig& sslConfig) { + if (sslConfig.useSsl) { #if WITH_SSL - socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str()); - socketFactory_->authenticate(false); + socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint.ip, endPoint.port); sslSocket->setConnTimeout(connectionTimeoutInMs); transport = std::make_shared(sslSocket); @@ -332,7 +331,7 @@ bool SessionConnection::reconnect() { } tryHostNum++; try { - init(this->endPoint, this->session->useSSL_, this->session->trustCertFilePath_); + init(this->endPoint, this->session->sslConfig_); reconnect = true; } catch (const IoTDBConnectionException& e) { log_warn("The current node may have been down, connection exception: %s", e.what()); diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.h b/iotdb-client/client-cpp/src/rpc/SessionConnection.h index 472e29fd6654..5216c96bd803 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.h +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.h @@ -53,7 +53,7 @@ class SessionConnection : public std::enable_shared_from_this const TEndPoint& getEndPoint(); - void init(const TEndPoint& endpoint, bool useSSL, const std::string& trustCertFilePath); + void init(const TEndPoint& endpoint, const SslConfig& sslConfig); void insertStringRecord(const TSInsertStringRecordReq& request); diff --git a/iotdb-client/client-cpp/src/rpc/SessionImpl.h b/iotdb-client/client-cpp/src/rpc/SessionImpl.h index 9fc3d9172934..b07f01b00869 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionImpl.h +++ b/iotdb-client/client-cpp/src/rpc/SessionImpl.h @@ -31,6 +31,7 @@ #include "DeviceID.h" #include "Endpoint.h" #include "NodesSupplier.h" +#include "RpcSslUtils.h" #include "Session.h" #include "SessionConnection.h" #include "ThriftConvert.h" @@ -41,8 +42,7 @@ class Session::Impl { public: std::string host_; int rpcPort_ = 6667; - bool useSSL_ = false; - std::string trustCertFilePath_; + SslConfig sslConfig_; std::vector nodeUrls_; std::string username_ = "root"; std::string password_ = "root"; diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp index 1cc6c5417b2d..c2a173865d34 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp @@ -17,6 +17,7 @@ * under the License. */ #include "ThriftConnection.h" +#include "RpcSslUtils.h" #include #include #include @@ -64,13 +65,11 @@ void ThriftConnection::initZoneId() { } void ThriftConnection::init(const std::string& username, const std::string& password, - bool enableRPCCompression, bool useSSL, - const std::string& trustCertFilePath, const std::string& zoneId, - const std::string& version) { - if (useSSL) { + bool enableRPCCompression, const SslConfig& sslConfig, + const std::string& zoneId, const std::string& version) { + if (sslConfig.useSsl) { #if WITH_SSL - socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str()); - socketFactory_->authenticate(false); + socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint_.ip, endPoint_.port); sslSocket->setConnTimeout(connectionTimeoutInMs_); transport_ = std::make_shared(sslSocket); diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h index 286911740316..495b74d77dd8 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h @@ -24,6 +24,7 @@ #include #endif #include "IClientRPCService.h" +#include "RpcSslUtils.h" #include "SessionConfig.h" class SessionDataSet; @@ -43,9 +44,8 @@ class ThriftConnection { ~ThriftConnection(); void init(const std::string& username, const std::string& password, - bool enableRPCCompression = false, bool useSSL = false, - const std::string& trustCertFilePath = "", const std::string& zoneId = std::string(), - const std::string& version = "V_1_0"); + bool enableRPCCompression = false, const SslConfig& sslConfig = SslConfig(), + const std::string& zoneId = std::string(), const std::string& version = "V_1_0"); std::unique_ptr executeQueryStatement(const std::string& sql, int64_t timeoutInMs = -1); diff --git a/iotdb-client/client-cpp/src/session/Session.cpp b/iotdb-client/client-cpp/src/session/Session.cpp index 7dd0a7813ed5..4b24b992461e 100644 --- a/iotdb-client/client-cpp/src/session/Session.cpp +++ b/iotdb-client/client-cpp/src/session/Session.cpp @@ -27,6 +27,7 @@ #include #include #include "SessionImpl.h" +#include "RpcSslUtils.h" #include "SessionDataSet.h" #include "ThriftConvert.h" @@ -541,8 +542,13 @@ Session::Session(AbstractSessionBuilder* builder) : impl_(new Impl()) { impl_->enableRedirection_ = builder->enableRedirections; impl_->connectTimeoutMs_ = builder->connectTimeoutMs; impl_->nodeUrls_ = builder->nodeUrls; - impl_->useSSL_ = builder->useSSL; - impl_->trustCertFilePath_ = builder->trustCertFilePath; + impl_->sslConfig_.useSsl = builder->useSSL; + impl_->sslConfig_.sslProtocol = builder->sslProtocol; + impl_->sslConfig_.trustStore = builder->trustStore; + impl_->sslConfig_.trustStorePwd = builder->trustStorePwd; + impl_->sslConfig_.keyStore = builder->keyStore; + impl_->sslConfig_.keyStorePwd = builder->keyStorePwd; + impl_->sslConfig_.trustCertFilePath = builder->trustCertFilePath; impl_->initZoneId(); impl_->initNodesSupplier(impl_->nodeUrls_); } @@ -555,6 +561,13 @@ void Session::setDatabase(const std::string& database) { impl_->database_ = database; } +void Session::setSslConfig(const SslConfig& sslConfig) { + if (!impl_->isClosed_) { + throw IoTDBException("Cannot change SSL configuration after Session is opened."); + } + impl_->sslConfig_ = sslConfig; +} + std::string Session::getDatabase() { return impl_->database_; } @@ -870,8 +883,7 @@ void Session::Impl::initNodesSupplier(const std::vector& nodeUrls) } if (enableAutoFetch_) { - nodesSupplier_ = - NodesSupplier::create(endPoints, username_, password_, useSSL_, trustCertFilePath_); + nodesSupplier_ = NodesSupplier::create(endPoints, username_, password_, sslConfig_); } else { nodesSupplier_ = make_shared(endPoints); } diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp b/iotdb-client/client-cpp/src/session/SessionC.cpp index 79287cf02523..ddf6c7fa73c2 100644 --- a/iotdb-client/client-cpp/src/session/SessionC.cpp +++ b/iotdb-client/client-cpp/src/session/SessionC.cpp @@ -24,6 +24,7 @@ #include "TableSessionBuilder.h" #include "SessionBuilder.h" #include "SessionDataSet.h" +#include "RpcSslUtils.h" #include #include @@ -39,10 +40,14 @@ struct CSession_ { std::shared_ptr cpp; + SslConfig sslConfig; + bool sslConfigured = false; }; struct CTableSession_ { std::shared_ptr cpp; + SslConfig sslConfig; + bool sslConfigured = false; }; struct CTablet_ { @@ -154,6 +159,32 @@ static std::map toStringMap(int count, const char* con return m; } +static void applyPendingSslConfig(CSession* session) { + if (session != nullptr && session->sslConfigured) { + session->cpp->setSslConfig(session->sslConfig); + } +} + +static void applyPendingSslConfig(CTableSession* session) { + if (session != nullptr && session->sslConfigured) { + session->cpp->setSslConfig(session->sslConfig); + } +} + +static TsStatus setSslStringField(std::string& field, const char* value, const char* label) { + if (value == nullptr) { + return setError(TS_ERR_INVALID_PARAM, std::string(label) + " is null"); + } + field = value; + return TS_OK; +} + +static std::shared_ptr createTableSession(TableSessionBuilder* builder) { + builder->sqlDialect = "table"; + auto session = std::make_shared(builder); + return std::make_shared(session); +} + /** * Convert C typed values (void* const* values, TSDataType_C* types, int count) * to C++ vector that Session expects. @@ -301,6 +332,7 @@ TsStatus ts_session_open(CSession* session) { if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(); return TS_OK; } catch (const std::exception& e) { @@ -313,6 +345,7 @@ TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompr if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(enableRPCCompression); return TS_OK; } catch (const std::exception& e) { @@ -332,6 +365,70 @@ TsStatus ts_session_close(CSession* session) { } } +TsStatus ts_session_set_use_ssl(CSession* session, bool useSsl) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + session->sslConfig.useSsl = useSsl; + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_ssl_protocol(CSession* session, const char* sslProtocol) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.sslProtocol, sslProtocol, "sslProtocol"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + +TsStatus ts_session_set_trust_store(CSession* session, const char* trustStore, + const char* trustStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustStore, trustStore, "trustStore"); + if (status != TS_OK) { + return status; + } + if (trustStorePwd != nullptr) { + session->sslConfig.trustStorePwd = trustStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, + const char* keyStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.keyStore, keyStore, "keyStore"); + if (status != TS_OK) { + return status; + } + if (keyStorePwd != nullptr) { + session->sslConfig.keyStorePwd = keyStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustCertFilePath, trustCertFilePath, + "trustCertFilePath"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + /* ============================================================ * Session Lifecycle — Table Model * ============================================================ */ @@ -340,17 +437,14 @@ CTableSession* ts_table_session_new(const char* host, int rpcPort, const char* u const char* password, const char* database) { clearError(); try { - std::unique_ptr builder(new TableSessionBuilder()); - auto tableSession = builder->host(std::string(host)) - ->rpcPort(rpcPort) - ->username(std::string(username)) - ->password(std::string(password)) - ->database(std::string(database ? database : "")) - ->build(); - CTableSession_ tmp{}; - tmp.cpp = std::move(tableSession); + TableSessionBuilder builder; + builder.host(std::string(host)) + ->rpcPort(rpcPort) + ->username(std::string(username)) + ->password(std::string(password)) + ->database(std::string(database ? database : "")); auto* cts = new CTableSession_(); - cts->cpp = std::move(tmp.cpp); + cts->cpp = createTableSession(&builder); return cts; } catch (const std::exception& e) { handleException(e); @@ -364,16 +458,13 @@ CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int clearError(); try { auto urls = toStringVec(nodeUrls, urlCount); - std::unique_ptr builder(new TableSessionBuilder()); - auto tableSession = builder->nodeUrls(urls) - ->username(std::string(username)) - ->password(std::string(password)) - ->database(std::string(database ? database : "")) - ->build(); - CTableSession_ tmp{}; - tmp.cpp = std::move(tableSession); + TableSessionBuilder builder; + builder.nodeUrls(urls) + ->username(std::string(username)) + ->password(std::string(password)) + ->database(std::string(database ? database : "")); auto* cts = new CTableSession_(); - cts->cpp = std::move(tmp.cpp); + cts->cpp = createTableSession(&builder); return cts; } catch (const std::exception& e) { handleException(e); @@ -390,6 +481,7 @@ TsStatus ts_table_session_open(CTableSession* session) { if (!session) return setError(TS_ERR_NULL_PTR, "session is null"); try { + applyPendingSslConfig(session); session->cpp->open(); return TS_OK; } catch (const std::exception& e) { @@ -409,6 +501,71 @@ TsStatus ts_table_session_close(CTableSession* session) { } } +TsStatus ts_table_session_set_use_ssl(CTableSession* session, bool useSsl) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + session->sslConfig.useSsl = useSsl; + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_ssl_protocol(CTableSession* session, const char* sslProtocol) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.sslProtocol, sslProtocol, "sslProtocol"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + +TsStatus ts_table_session_set_trust_store(CTableSession* session, const char* trustStore, + const char* trustStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustStore, trustStore, "trustStore"); + if (status != TS_OK) { + return status; + } + if (trustStorePwd != nullptr) { + session->sslConfig.trustStorePwd = trustStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyStore, + const char* keyStorePwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.keyStore, keyStore, "keyStore"); + if (status != TS_OK) { + return status; + } + if (keyStorePwd != nullptr) { + session->sslConfig.keyStorePwd = keyStorePwd; + } + session->sslConfigured = true; + return TS_OK; +} + +TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, + const char* trustCertFilePath) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = setSslStringField(session->sslConfig.trustCertFilePath, trustCertFilePath, + "trustCertFilePath"); + if (status == TS_OK) { + session->sslConfigured = true; + } + return status; +} + /* ============================================================ * Timezone * ============================================================ */ diff --git a/iotdb-client/client-cpp/src/session/SessionPool.cpp b/iotdb-client/client-cpp/src/session/SessionPool.cpp index a828f0ac2c6d..42961dbaff61 100644 --- a/iotdb-client/client-cpp/src/session/SessionPool.cpp +++ b/iotdb-client/client-cpp/src/session/SessionPool.cpp @@ -109,6 +109,31 @@ SessionPool& SessionPool::setTrustCertFilePath(std::string path) { return *this; } +SessionPool& SessionPool::setSslProtocol(std::string sslProtocol) { + sslProtocol_ = std::move(sslProtocol); + return *this; +} + +SessionPool& SessionPool::setTrustStore(std::string trustStore) { + trustStore_ = std::move(trustStore); + return *this; +} + +SessionPool& SessionPool::setTrustStorePwd(std::string trustStorePwd) { + trustStorePwd_ = std::move(trustStorePwd); + return *this; +} + +SessionPool& SessionPool::setKeyStore(std::string keyStore) { + keyStore_ = std::move(keyStore); + return *this; +} + +SessionPool& SessionPool::setKeyStorePwd(std::string keyStorePwd) { + keyStorePwd_ = std::move(keyStorePwd); + return *this; +} + std::shared_ptr SessionPool::constructNewSession() { AbstractSessionBuilder builder; builder.host = host_; @@ -126,6 +151,11 @@ std::shared_ptr SessionPool::constructNewSession() { builder.connectTimeoutMs = connectTimeoutMs_; builder.useSSL = useSSL_; builder.trustCertFilePath = trustCertFilePath_; + builder.sslProtocol = sslProtocol_; + builder.trustStore = trustStore_; + builder.trustStorePwd = trustStorePwd_; + builder.keyStore = keyStore_; + builder.keyStorePwd = keyStorePwd_; auto session = std::make_shared(&builder); session->open(enableRPCCompression_, connectTimeoutMs_); diff --git a/iotdb-client/client-cpp/src/session/TableSession.cpp b/iotdb-client/client-cpp/src/session/TableSession.cpp index 9cd80b7dd789..4c7fc9b5edb1 100644 --- a/iotdb-client/client-cpp/src/session/TableSession.cpp +++ b/iotdb-client/client-cpp/src/session/TableSession.cpp @@ -20,6 +20,7 @@ // This file is a translation of the Java file iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java #include "TableSession.h" +#include "RpcSslUtils.h" #include "SessionDataSet.h" void TableSession::insert(Tablet& tablet, bool sorted) { @@ -43,4 +44,8 @@ void TableSession::open(bool enableRPCCompression) { } void TableSession::close() { session_->close(); +} + +void TableSession::setSslConfig(const SslConfig& sslConfig) { + session_->setSslConfig(sslConfig); } \ No newline at end of file diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index 9d5428edc4b8..31baa94679c2 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -38,20 +38,37 @@ if(NOT EXISTS "${_catch2_header}") file(DOWNLOAD "${CATCH2_URL}" "${_catch2_header}" SHOW_PROGRESS TLS_VERIFY ON) endif() -set(_test_targets +set(_plain_test_targets session_tests session_relational_tests session_c_tests session_c_relational_tests) +set(_rpc_ssl_test_targets rpc_ssl_utils_tests rpc_ntls_utils_tests) + +set(_test_targets ${_plain_test_targets} ${_rpc_ssl_test_targets}) + add_executable(session_tests main.cpp cpp/sessionIT.cpp) add_executable(session_relational_tests main_Relational.cpp cpp/sessionRelationalIT.cpp) add_executable(session_c_tests main_c.cpp cpp/sessionCIT.cpp) add_executable(session_c_relational_tests main_c_Relational.cpp cpp/sessionCRelationalIT.cpp) +add_executable(rpc_ssl_utils_tests + main_rpc_ssl.cpp + cpp/RpcSslUtilsTest.cpp + cpp/RpcSslTlsMutualAuthTest.cpp + cpp/RpcSslIotdbE2eTest.cpp + cpp/SslTestFixtures.cpp + cpp/ItSslConnection.cpp) +add_executable(rpc_ntls_utils_tests + main_rpc_ntls.cpp + cpp/RpcSslTlcpMutualAuthTest.cpp + cpp/RpcNtlsE2eTest.cpp + cpp/SslTestFixtures.cpp) foreach(_t IN LISTS _test_targets) target_include_directories(${_t} PRIVATE "${_catch2_include_dir}" + "${CMAKE_CURRENT_SOURCE_DIR}/cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/rpc" "${THRIFT_GEN_CPP_DIR}" "${THRIFT_INCLUDE_DIR}") @@ -61,11 +78,64 @@ foreach(_t IN LISTS _test_targets) target_link_libraries(${_t} PRIVATE iotdb_session) if(WITH_SSL) target_link_libraries(${_t} PRIVATE OpenSSL::SSL OpenSSL::Crypto) + if(TARGET iotdb_tongsuo_openssl_wrap) + target_link_libraries(${_t} PRIVATE iotdb_tongsuo_openssl_wrap) + endif() endif() endforeach() -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC) +# macOS: apply Tongsuo openssl header wrap to test targets (same as iotdb_session). +if(WITH_SSL AND TARGET iotdb_tongsuo_openssl_wrap) foreach(_t IN LISTS _test_targets) + target_include_directories(${_t} BEFORE PRIVATE + "${CMAKE_BINARY_DIR}/generated/tongsuo-openssl-wrap") + endforeach() +endif() + +if(WITH_SSL) + target_compile_definitions(rpc_ssl_utils_tests PRIVATE IOTDB_RPC_SSL_IT=1) + + if(WIN32) + set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl.exe") + else() + set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl") + endif() + file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" _iotdb_test_fixtures_dir) + file(TO_CMAKE_PATH "${_iotdb_openssl_executable}" _iotdb_openssl_executable_cmake) + file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir) + string(REPLACE "\\" "/" _iotdb_test_fixtures_dir_fwd "${_iotdb_test_fixtures_dir}") + string(REPLACE "\\" "/" _iotdb_openssl_executable_fwd "${_iotdb_openssl_executable_cmake}") + string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir}") + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_compile_definitions(${_t} PRIVATE + IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir_fwd}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/fixtures" + "$/fixtures" + COMMENT "Copy SSL test fixtures next to ${_t}") + endforeach() + target_compile_definitions(rpc_ssl_utils_tests PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") + target_compile_definitions(rpc_ntls_utils_tests PRIVATE + IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" + IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") + if(WIN32) + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") + endforeach() + else() + foreach(_t IN LISTS _rpc_ssl_test_targets) + target_link_libraries(${_t} PRIVATE iotdb_thrift_static) + endforeach() + endif() +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC AND NOT APPLE) + set(_iotdb_asan_targets session_tests session_relational_tests session_c_tests + session_c_relational_tests rpc_ssl_utils_tests rpc_ntls_utils_tests) + foreach(_t IN LISTS _iotdb_asan_targets) target_compile_options(${_t} PRIVATE -fsanitize=address -fno-omit-frame-pointer) target_link_options(${_t} PRIVATE -fsanitize=address) endforeach() @@ -86,19 +156,53 @@ if(MSVC) add_test(NAME sessionRelationalIT CONFIGURATIONS Release COMMAND session_relational_tests) add_test(NAME sessionCIT CONFIGURATIONS Release COMMAND session_c_tests) add_test(NAME sessionCRelationalIT CONFIGURATIONS Release COMMAND session_c_relational_tests) + add_test(NAME rpcSslUtilsTest CONFIGURATIONS Release COMMAND rpc_ssl_utils_tests) + add_test(NAME rpcNtlsUtilsTest CONFIGURATIONS Release COMMAND rpc_ntls_utils_tests) foreach(_t IN LISTS _test_targets) add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + if(WITH_SSL AND ${_t} IN_LIST _rpc_ssl_test_targets) + _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls) + foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_dll}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endforeach() + endif() endforeach() else() add_test(NAME sessionIT COMMAND session_tests) add_test(NAME sessionRelationalIT COMMAND session_relational_tests) add_test(NAME sessionCIT COMMAND session_c_tests) add_test(NAME sessionCRelationalIT COMMAND session_c_relational_tests) + add_test(NAME rpcSslUtilsTest COMMAND rpc_ssl_utils_tests) + add_test(NAME rpcNtlsUtilsTest COMMAND rpc_ntls_utils_tests) + foreach(_t IN LISTS _test_targets) + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $ + COMMENT "Copy IoTDB runtime library next to ${_t}") + if(WITH_SSL AND ${_t} IN_LIST _rpc_ssl_test_targets) + foreach(_ssl_lib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY) + if(_ssl_lib AND EXISTS "${_ssl_lib}") + add_custom_command(TARGET ${_t} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_ssl_lib}" $ + COMMENT "Copy bundled SSL runtime next to ${_t}") + endif() + endforeach() + endif() + endforeach() endif() -# Run sequentially: parallel ctest overloads the single local IoTDB instance. set_tests_properties( sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT - PROPERTIES RUN_SERIAL TRUE) + PROPERTIES LABELS "plain" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +set_tests_properties( + rpcSslUtilsTest + PROPERTIES LABELS "ssl" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +set_tests_properties( + rpcNtlsUtilsTest + PROPERTIES LABELS "ntls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp new file mode 100644 index 000000000000..4b9f15e76b33 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp @@ -0,0 +1,180 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ItSslConnection.h" + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) + +#include +#include + +#include "SessionPool.h" + +#if defined(_WIN32) +#include +#else +#include +#endif + +namespace { + +constexpr const char* kStorePassword = "thrift"; + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string fixturesRoot() { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + if (pathExists(joinPath(configured, "tls/tls-trust.p12")) || + pathExists(joinPath(configured, "tls\\tls-trust.p12"))) { + return configured; + } +#endif + const std::string copied = joinPath(executableDir(), "fixtures"); + if (pathExists(joinPath(copied, "tls/tls-trust.p12")) || + pathExists(joinPath(copied, "tls\\tls-trust.p12"))) { + return copied; + } +#ifdef IOTDB_TEST_FIXTURES_DIR + return IOTDB_TEST_FIXTURES_DIR; +#else + return copied; +#endif +} + +std::string tlsTrustStorePath() { + static const std::string path = joinPath(joinPath(fixturesRoot(), "tls"), "tls-trust.p12"); + return path; +} + +} // namespace + +void it_ssl_configure_tree_session(CSession* session) { + if (session == nullptr) { + return; + } + ts_session_set_use_ssl(session, true); + ts_session_set_ssl_protocol(session, "TLS"); + ts_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); +} + +void it_ssl_configure_table_session(CTableSession* session) { + if (session == nullptr) { + return; + } + ts_table_session_set_use_ssl(session, true); + ts_table_session_set_ssl_protocol(session, "TLS"); + ts_table_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); +} + +namespace itssl { + +void configureSessionBuilder(SessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureTableSessionBuilder(TableSessionBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +void configureSessionPoolBuilder(SessionPoolBuilder& builder) { + builder.useSSL(true) + ->sslProtocol("TLS") + ->trustStore(tlsTrustStorePath()) + ->trustStorePwd(kStorePassword); +} + +std::shared_ptr newOpenedTreeSession() { + SessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + configureSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(false); + return session; +} + +std::shared_ptr newOpenedTableSession() { + TableSessionBuilder builder; + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + configureTableSessionBuilder(builder); + std::shared_ptr session = builder.build(); + session->open(); + return session; +} + +} // namespace itssl + +#else // WITH_SSL && IOTDB_RPC_SSL_IT + +void it_ssl_configure_tree_session(CSession* session) { + (void)session; +} + +void it_ssl_configure_table_session(CTableSession* session) { + (void)session; +} + +#endif // WITH_SSL && IOTDB_RPC_SSL_IT diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.h b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h new file mode 100644 index 000000000000..9818fd076439 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_IT_SSL_CONNECTION_H +#define IOTDB_IT_SSL_CONNECTION_H + +#include "SessionC.h" + +#ifdef __cplusplus +#include + +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionPool.h" +#include "TableSession.h" +#include "TableSessionBuilder.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** Apply one-way TLS settings for integration tests against a TLS-enabled IoTDB. */ +void it_ssl_configure_tree_session(CSession* session); +void it_ssl_configure_table_session(CTableSession* session); + +#ifdef __cplusplus +} + +namespace itssl { + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) +void configureSessionBuilder(SessionBuilder& builder); +void configureTableSessionBuilder(TableSessionBuilder& builder); +void configureSessionPoolBuilder(SessionPoolBuilder& builder); +std::shared_ptr newOpenedTreeSession(); +std::shared_ptr newOpenedTableSession(); +#endif + +} // namespace itssl +#endif + +#endif // IOTDB_IT_SSL_CONNECTION_H diff --git a/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp new file mode 100644 index 000000000000..9bf6558f6e23 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include +#include +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +SslConfig tlcpTrustOnlyConfig() { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + return config; +} + +SslConfig tlcpMutualConfig() { + SslConfig config = tlcpTrustOnlyConfig(); + config.keyStore = ssltest::buildTlcpDualKeyStoreP12(); + config.keyStorePwd = ssltest::kStorePassword; + return config; +} + +bool startTlcpServer(ssltest::OpenSslServerProcess& server, bool requireClientCert) { + const std::string caFile = ssltest::tlcpFixture("ca.crt"); + const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); + const std::string signKey = ssltest::tlcpFixture("server_sign.key"); + const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); + const std::string encKey = ssltest::tlcpFixture("server_enc.key"); + if (!fixtureExists(caFile) || !fixtureExists(signCert) || !fixtureExists(signKey) || + !fixtureExists(encCert) || !fixtureExists(encKey)) { + return false; + } + + std::vector args = { + "-enable_ntls", + "-ntls", + "-CAfile", caFile, + "-sign_cert", signCert, + "-sign_key", signKey, + "-enc_cert", encCert, + "-enc_key", encKey, + "-www", + }; + if (requireClientCert) { + args.push_back("-Verify"); + args.push_back("1"); + } + return server.start(args) && server.running() && server.port() > 0; +} + +} // namespace + +TEST_CASE("TLCP one-way handshake with openssl NTLS s_server", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, false)); + REQUIRE(ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLCP one-way auth fails when server requires client certificate", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, true)); + REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLCP mutual auth handshake with dual PKCS12 client store", "[rpc][ntls][e2e]") { +#if WITH_SSL + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, true)); + const SslConfig config = tlcpMutualConfig(); + REQUIRE_FALSE(config.keyStore.empty()); + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); + server.stop(); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp new file mode 100644 index 000000000000..ef77b53034b6 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp @@ -0,0 +1,175 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include +#include + +#include "Common.h" +#include "ItSslConnection.h" +#include "Session.h" +#include "SessionBuilder.h" +#include "SessionC.h" +#include "SessionDataSet.h" +#include "SslTestFixtures.h" +#include "TableSessionBuilder.h" + +#if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) + +TEST_CASE("TLS tree Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb][e2e]") { + auto session = itssl::newOpenedTreeSession(); + REQUIRE(session != nullptr); + + const std::string database = "root.cpp_ssl_it_tree"; + const std::string timeseries = database + ".d1.s1"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + try { + session->deleteStorageGroup(database); + } catch (...) { + } + + session->setStorageGroup(database); + session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, CompressionType::UNCOMPRESSED); + session->insertRecord(database + ".d1", 1, {"s1"}, {"1"}); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT s1 FROM " + database + ".d1")); + REQUIRE(dataSet != nullptr); + REQUIRE(dataSet->hasNext()); + std::shared_ptr record = dataSet->next(); + REQUIRE(record != nullptr); + REQUIRE(record->timestamp == 1); + REQUIRE(record->fields.size() == 1); + REQUIRE(record->fields[0].intV.value() == 1); + REQUIRE_FALSE(dataSet->hasNext()); + dataSet->closeOperationHandle(); + + session->deleteTimeseries(timeseries); + session->deleteStorageGroup(database); + session->close(); +} + +TEST_CASE("TLS table Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb][e2e]") { + auto session = itssl::newOpenedTableSession(); + REQUIRE(session != nullptr); + + session->executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS cpp_ssl_it_table"); + session->executeNonQueryStatement("USE cpp_ssl_it_table"); + session->executeNonQueryStatement( + "CREATE TABLE IF NOT EXISTS ssl_it_table (tag1 STRING TAG, value INT32 FIELD)"); + session->executeNonQueryStatement("INSERT INTO ssl_it_table(time, tag1, value) VALUES (1, 't1', 42)"); + + std::unique_ptr dataSet( + session->executeQueryStatement("SELECT time, value FROM ssl_it_table WHERE tag1 = 't1'")); + REQUIRE(dataSet != nullptr); + REQUIRE(dataSet->hasNext()); + std::shared_ptr record = dataSet->next(); + REQUIRE(record != nullptr); + REQUIRE(record->fields.size() == 2); + REQUIRE(record->fields[0].longV.value() == 1); + REQUIRE(record->fields[1].intV.value() == 42); + REQUIRE_FALSE(dataSet->hasNext()); + dataSet->closeOperationHandle(); + + session->executeNonQueryStatement("DROP DATABASE IF EXISTS cpp_ssl_it_table"); + session->close(); +} + +TEST_CASE("TLS C tree Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { + CSession* session = ts_session_new("127.0.0.1", 6667, "root", "root"); + REQUIRE(session != nullptr); + it_ssl_configure_tree_session(session); + REQUIRE(ts_session_open(session) == TS_OK); + + const char* path = "root.cpp_ssl_it_c.d1.s1"; + bool exists = false; + REQUIRE(ts_session_check_timeseries_exists(session, path, &exists) == TS_OK); + if (exists) { + REQUIRE(ts_session_delete_timeseries(session, path) == TS_OK); + } + + REQUIRE(ts_session_create_database(session, "root.cpp_ssl_it_c") == TS_OK); + REQUIRE(ts_session_create_timeseries(session, path, TS_TYPE_INT32, TS_ENCODING_PLAIN, + TS_COMPRESSION_UNCOMPRESSED) == TS_OK); + const char* measurements[] = {"s1"}; + const char* values[] = {"1"}; + REQUIRE(ts_session_insert_record_str(session, "root.cpp_ssl_it_c.d1", 1, 1, measurements, values) == + TS_OK); + + CSessionDataSet* dataSet = nullptr; + REQUIRE(ts_session_execute_query(session, "SELECT s1 FROM root.cpp_ssl_it_c.d1", &dataSet) == TS_OK); + REQUIRE(dataSet != nullptr); + REQUIRE(ts_dataset_has_next(dataSet)); + CRowRecord* record = ts_dataset_next(dataSet); + REQUIRE(record != nullptr); + REQUIRE(ts_row_record_get_timestamp(record) == 1); + REQUIRE(ts_row_record_get_field_count(record) == 1); + REQUIRE(ts_row_record_get_int32(record, 0) == 1); + ts_row_record_destroy(record); + REQUIRE_FALSE(ts_dataset_has_next(dataSet)); + ts_dataset_destroy(dataSet); + + REQUIRE(ts_session_delete_timeseries(session, path) == TS_OK); + REQUIRE(ts_session_delete_database(session, "root.cpp_ssl_it_c") == TS_OK); + REQUIRE(ts_session_close(session) == TS_OK); + ts_session_destroy(session); +} + +TEST_CASE("TLS C table Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { + CTableSession* session = ts_table_session_new("127.0.0.1", 6667, "root", "root", ""); + REQUIRE(session != nullptr); + it_ssl_configure_table_session(session); + REQUIRE(ts_table_session_open(session) == TS_OK); + + REQUIRE(ts_table_session_execute_non_query(session, "CREATE DATABASE IF NOT EXISTS cpp_ssl_it_c_table") == + TS_OK); + REQUIRE(ts_table_session_execute_non_query(session, "USE cpp_ssl_it_c_table") == TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, + "CREATE TABLE IF NOT EXISTS ssl_it_c_table (tag1 STRING TAG, value INT32 FIELD)") == TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, "INSERT INTO ssl_it_c_table(time, tag1, value) VALUES (1, 't1', 42)") == TS_OK); + + CSessionDataSet* dataSet = nullptr; + REQUIRE(ts_table_session_execute_query( + session, "SELECT time, value FROM ssl_it_c_table WHERE tag1 = 't1'", &dataSet) == TS_OK); + REQUIRE(dataSet != nullptr); + REQUIRE(ts_dataset_has_next(dataSet)); + CRowRecord* record = ts_dataset_next(dataSet); + REQUIRE(record != nullptr); + REQUIRE(ts_row_record_get_field_count(record) >= 2); + REQUIRE(ts_row_record_get_int64(record, 0) == 1); + REQUIRE(ts_row_record_get_int32(record, 1) == 42); + ts_row_record_destroy(record); + ts_dataset_destroy(dataSet); + + REQUIRE(ts_table_session_execute_non_query(session, "DROP DATABASE IF EXISTS cpp_ssl_it_c_table") == TS_OK); + REQUIRE(ts_table_session_close(session) == TS_OK); + ts_table_session_destroy(session); +} + +TEST_CASE("Plain client cannot connect to TLS-enabled IoTDB", "[rpc][ssl][iotdb][e2e]") { + Session session("127.0.0.1", 6667, "root", "root"); + REQUIRE_THROWS_AS(session.open(false), IoTDBException); +} + +#endif // WITH_SSL && IOTDB_RPC_SSL_IT diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp new file mode 100644 index 000000000000..f749d661b421 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl][mutual]") { +#if WITH_SSL + const std::string trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + REQUIRE(fixtureExists(trustStore)); + const std::string keyStore = ssltest::buildTlcpDualKeyStoreP12(); + REQUIRE_FALSE(keyStore.empty()); + REQUIRE(fixtureExists(keyStore)); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = trustStore; + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); + REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp new file mode 100644 index 000000000000..fcf96f51dddc --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp @@ -0,0 +1,167 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include + +#include "Common.h" +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +namespace { + +bool fixtureExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +} // namespace + +TEST_CASE("TLS mutual auth creates client SSL_CTX with trust and key stores", "[rpc][ssl][mutual]") { +#if WITH_SSL + const std::string trustStore = ssltest::tlsFixture("tls-trust.p12"); + const std::string keyStore = ssltest::tlsFixture("tls-client.p12"); + REQUIRE(fixtureExists(trustStore)); + REQUIRE(fixtureExists(keyStore)); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = trustStore; + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = keyStore; + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE_NOTHROW(RpcSslUtils::validateTrustStore(trustStore, config.trustStorePwd)); + REQUIRE_NOTHROW(RpcSslUtils::validateKeyStore(keyStore, config.keyStorePwd)); + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + REQUIRE(ssltest::sslContextHasClientCertificate(ctx)); + SSL_CTX_free(ctx); +#endif +} + +TEST_CASE("TLS mutual auth handshake with openssl s_server", "[rpc][ssl][mutual][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlsFixture("ca.crt"); + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(caFile)); + REQUIRE(fixtureExists(serverCert)); + REQUIRE(fixtureExists(serverKey)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-tls1_2", + "-Verify", "1", + "-CAfile", caFile, + "-cert", serverCert, + "-key", serverKey, + "-www", + }); + REQUIRE(started); + REQUIRE(server.running()); + REQUIRE(server.port() > 0); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = ssltest::tlsFixture("tls-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + config.keyStore = ssltest::tlsFixture("tls-client.p12"); + config.keyStorePwd = ssltest::kStorePassword; + + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("TLS one-way auth fails when server requires client certificate", "[rpc][ssl][mutual][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlsFixture("ca.crt"); + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(caFile)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-tls1_2", + "-Verify", "1", + "-CAfile", caFile, + "-cert", serverCert, + "-key", serverKey, + "-www", + }); + REQUIRE(started); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = ssltest::tlsFixture("tls-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + + REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("Thrift TSSLSocketFactory verifies server certificate when trust store is set", + "[rpc][ssl][thrift][e2e]") { +#if WITH_SSL + const std::string caFile = ssltest::tlsFixture("ca.crt"); + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(caFile)); + REQUIRE(fixtureExists(serverCert)); + REQUIRE(fixtureExists(serverKey)); + + ssltest::OpenSslServerProcess server; + const bool started = server.start({ + "-tls1_2", + "-CAfile", caFile, + "-cert", serverCert, + "-key", serverKey, + "-www", + }); + REQUIRE(started); + REQUIRE(server.port() > 0); + + SslConfig goodConfig; + goodConfig.useSsl = true; + goodConfig.sslProtocol = "TLS"; + goodConfig.trustStore = ssltest::tlsFixture("tls-trust.p12"); + goodConfig.trustStorePwd = ssltest::kStorePassword; + REQUIRE(ssltest::thriftTlsHandshakeWithSslConfig(goodConfig, "127.0.0.1", server.port())); + + SslConfig badConfig = goodConfig; + badConfig.trustStore = ssltest::tlsFixture("tls-client.p12"); + badConfig.keyStore.clear(); + REQUIRE_FALSE(ssltest::thriftTlsHandshakeWithSslConfig(badConfig, "127.0.0.1", server.port())); + server.stop(); +#endif +} + +TEST_CASE("RpcSslUtils rejects JKS store paths", "[rpc][ssl]") { +#if WITH_SSL + REQUIRE_THROWS_AS(RpcSslUtils::validateTrustStore("/path/to/trust.jks", "pwd"), IoTDBException); + REQUIRE_THROWS_AS(RpcSslUtils::validateKeyStore("/path/to/client.jks", "pwd"), IoTDBException); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp new file mode 100644 index 000000000000..54b48e67667e --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include + +#include "Common.h" +#include "RpcSslUtils.h" + +TEST_CASE("RpcSslUtils protocol helpers", "[rpc][ssl]") { + REQUIRE(RpcSslUtils::normalizeProtocol("") == "TLS"); + REQUIRE(RpcSslUtils::normalizeProtocol(" TLSv1.3 ") == "TLSv1.3"); + REQUIRE(RpcSslUtils::isTlcpProtocol("TLCP") == true); + REQUIRE(RpcSslUtils::isTlcpProtocol(" tlcp1.1 ") == true); + REQUIRE(RpcSslUtils::isTlcpProtocol("TLS") == false); + + const std::string origin = RpcSslUtils::getProtocol(); + RpcSslUtils::configure("ConfiguredProtocol"); + REQUIRE(RpcSslUtils::resolveProtocol("") == "ConfiguredProtocol"); + REQUIRE(RpcSslUtils::resolveProtocol(" ExplicitProtocol ") == "ExplicitProtocol"); + RpcSslUtils::configure(origin); +} + +TEST_CASE("SslConfig effectiveTrustStore backward compatibility", "[rpc][ssl]") { + SslConfig config; + config.trustStore = "/path/to/trust.p12"; + config.trustCertFilePath = "/legacy/ca.pem"; + REQUIRE(config.effectiveTrustStore() == "/path/to/trust.p12"); + + config.trustStore.clear(); + config.trustCertFilePath = "/legacy/ca.pem"; + REQUIRE(config.effectiveTrustStore() == "/legacy/ca.pem"); +} + +TEST_CASE("RpcSslUtils store validation rejects missing files", "[rpc][ssl]") { + REQUIRE_THROWS_AS(RpcSslUtils::validateTrustStore("/path/does/not/exist.pem", ""), + IoTDBException); + REQUIRE_THROWS_AS(RpcSslUtils::validateKeyStore("/path/does/not/exist.p12", "pwd"), + IoTDBException); +} + +#if WITH_SSL +TEST_CASE("RpcSslUtils createClientSslContext for TLS without trust store", "[rpc][ssl]") { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + SSL_CTX_free(ctx); +} +#endif diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp new file mode 100644 index 000000000000..9c4a02bc8fa1 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -0,0 +1,705 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#include +#endif +#endif + +#if WITH_SSL +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "RpcSslUtils.h" +#include "SslTestFixtures.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ssltest { +namespace { + +std::string joinPath(const std::string& base, const std::string& name) { +#if defined(_WIN32) + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (base.empty()) { + return name; + } + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +std::string executableDir() { +#if defined(_WIN32) + char buffer[MAX_PATH]; + const DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return "."; + } + std::string path(buffer, len); + const auto pos = path.find_last_of("\\/"); + return pos == std::string::npos ? "." : path.substr(0, pos); +#elif defined(__APPLE__) + char buffer[PATH_MAX]; + uint32_t size = sizeof(buffer); + if (_NSGetExecutablePath(buffer, &size) != 0) { + return "."; + } + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#else + char buffer[4096]; + const ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len <= 0) { + return "."; + } + buffer[len] = '\0'; + std::string path(buffer); + const auto pos = path.find_last_of('/'); + return pos == std::string::npos ? "." : path.substr(0, pos); +#endif +} + +bool pathExists(const std::string& path) { + std::ifstream in(path.c_str(), std::ios::binary); + return in.good(); +} + +std::string configuredOrEmpty() { +#ifdef IOTDB_TEST_FIXTURES_DIR + return IOTDB_TEST_FIXTURES_DIR; +#else + return joinPath(executableDir(), "fixtures"); +#endif +} + +std::string firstExistingRoot() { +#ifdef IOTDB_TEST_FIXTURES_DIR + const std::string configured = IOTDB_TEST_FIXTURES_DIR; + if (pathExists(joinPath(configured, "tls/tls-trust.p12")) || + pathExists(joinPath(configured, "tls\\tls-trust.p12"))) { + return configured; + } +#endif + const std::string copied = joinPath(executableDir(), "fixtures"); + if (pathExists(joinPath(copied, "tls/tls-trust.p12")) || + pathExists(joinPath(copied, "tls\\tls-trust.p12"))) { + return copied; + } + return configuredOrEmpty(); +} + +#if WITH_SSL +EVP_PKEY* readPrivateKeyPem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return key; +} + +X509* readCertificatePem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return cert; +} + +void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { + unsigned char keyid[EVP_MAX_MD_SIZE]; + unsigned int keyidLen = 0; + if (X509_pubkey_digest(cert, EVP_sha1(), keyid, &keyidLen) == 1) { + PKCS12_add_localkeyid(bag, keyid, static_cast(keyidLen)); + } +} + +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, + const char* friendlyName, const std::string& password) { + PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); + PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); + addLocalKeyId(certbag, cert); + sk_PKCS12_SAFEBAG_push(bags, certbag); + + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + if (p8 == nullptr) { + return; + } + PKCS12_SAFEBAG* keybag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (keybag == nullptr) { + return; + } + PKCS12_add_friendlyname_utf8(keybag, friendlyName, -1); + addLocalKeyId(keybag, cert); + sk_PKCS12_SAFEBAG_push(bags, keybag); +} + +bool writePkcs12File(PKCS12* p12, const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "wb"); + if (bio == nullptr) { + return false; + } + const int rc = i2d_PKCS12_bio(bio, p12); + BIO_free(bio); + return rc == 1; +} + +PKCS12* readPkcs12File(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + PKCS12* p12 = d2i_PKCS12_bio(bio, nullptr); + BIO_free(bio); + return p12; +} + +void forEachPkcs12Bag(PKCS12* p12, const std::string& password, + const std::function& visitor) { + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(p12); + if (safes == nullptr) { + return; + } + for (int i = 0; i < sk_PKCS7_num(safes); ++i) { + PKCS7* p7 = sk_PKCS7_value(safes, i); + STACK_OF(PKCS12_SAFEBAG)* bags = nullptr; + if (PKCS7_type_is_data(p7)) { + bags = PKCS12_unpack_p7data(p7); + } else if (PKCS7_type_is_encrypted(p7)) { + bags = PKCS12_unpack_p7encdata(p7, password.c_str(), static_cast(password.size())); + } + if (bags == nullptr) { + continue; + } + for (int j = 0; j < sk_PKCS12_SAFEBAG_num(bags); ++j) { + visitor(sk_PKCS12_SAFEBAG_value(bags, j)); + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + sk_PKCS7_pop_free(safes, PKCS7_free); +} + +void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, + const std::string& password) { + forEachPkcs12Bag(source, password, [&](PKCS12_SAFEBAG* bag) { + const int bagType = PKCS12_SAFEBAG_get_nid(bag); + char* friendlyName = PKCS12_get_friendlyname(bag); + if (bagType == NID_certBag) { + X509* cert = PKCS12_certbag2x509(bag); + if (cert != nullptr) { + PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_cert(cert); + X509_free(cert); + if (newBag != nullptr) { + if (friendlyName != nullptr) { + PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); + } + sk_PKCS12_SAFEBAG_push(target, newBag); + } + } + } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { + EVP_PKEY* key = nullptr; + if (bagType == NID_pkcs8ShroudedKeyBag) { + PKCS8_PRIV_KEY_INFO* p8 = PKCS12_decrypt_skey(bag, password.c_str(), + static_cast(password.size())); + if (p8 != nullptr) { + key = EVP_PKCS82PKEY(p8); + PKCS8_PRIV_KEY_INFO_free(p8); + } + } else { + const PKCS8_PRIV_KEY_INFO* p8 = PKCS12_SAFEBAG_get0_p8inf(bag); + if (p8 != nullptr) { + key = EVP_PKCS82PKEY(p8); + } + } + if (key != nullptr) { + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + EVP_PKEY_free(key); + if (p8 != nullptr) { + PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, + p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (newBag != nullptr) { + if (friendlyName != nullptr) { + PKCS12_add_friendlyname_utf8(newBag, friendlyName, -1); + } + sk_PKCS12_SAFEBAG_push(target, newBag); + } + } + } + } + if (friendlyName != nullptr) { + OPENSSL_free(friendlyName); + } + }); +} + +std::string opensslExecutable() { +#ifdef IOTDB_OPENSSL_EXECUTABLE + return IOTDB_OPENSSL_EXECUTABLE; +#else + return "openssl"; +#endif +} + +#if !defined(_WIN32) +void prependOpenSslRuntimeToLdLibraryPath() { +#ifdef IOTDB_OPENSSL_ROOT_DIR + const std::string root = IOTDB_OPENSSL_ROOT_DIR; + std::string libPath = joinPath(root, "lib64"); + const std::string lib = joinPath(root, "lib"); + if (pathExists(lib)) { + libPath = libPath + ":" + lib; + } + const char* existing = std::getenv("LD_LIBRARY_PATH"); + if (existing != nullptr && existing[0] != '\0') { + libPath = libPath + ":" + existing; + } + setenv("LD_LIBRARY_PATH", libPath.c_str(), 1); +#if defined(__APPLE__) + const char* dyldExisting = std::getenv("DYLD_LIBRARY_PATH"); + std::string dyldPath = libPath; + if (dyldExisting != nullptr && dyldExisting[0] != '\0') { + dyldPath = dyldPath + ":" + dyldExisting; + } + setenv("DYLD_LIBRARY_PATH", dyldPath.c_str(), 1); +#endif +#endif +} +#endif + +std::string quoteArg(const std::string& arg) { +#if defined(_WIN32) + return "\"" + arg + "\""; +#else + if (arg.find(' ') != std::string::npos) { + return "\"" + arg + "\""; + } + return arg; +#endif +} + +} // namespace + +std::string fixturesRoot() { + return firstExistingRoot(); +} + +std::string tlsFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tls"), name); +} + +std::string tlcpFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tlcp"), name); +} + +std::string buildTlcpDualKeyStoreP12() { + const std::string password = kStorePassword; + const std::string outPath = joinPath(executableDir(), "tlcp-client-dual.p12"); + + PKCS12* signStore = readPkcs12File(tlcpFixture("tlcp-client-sign.p12")); + PKCS12* encStore = readPkcs12File(tlcpFixture("tlcp-client-enc.p12")); + if (signStore == nullptr || encStore == nullptr) { + if (signStore != nullptr) { + PKCS12_free(signStore); + } + if (encStore != nullptr) { + PKCS12_free(encStore); + } + return ""; + } + + STACK_OF(PKCS7)* safes = PKCS12_unpack_authsafes(signStore); + STACK_OF(PKCS7)* encSafes = PKCS12_unpack_authsafes(encStore); + PKCS12_free(signStore); + PKCS12_free(encStore); + if (safes == nullptr || encSafes == nullptr) { + if (safes != nullptr) { + sk_PKCS7_pop_free(safes, PKCS7_free); + } + if (encSafes != nullptr) { + sk_PKCS7_pop_free(encSafes, PKCS7_free); + } + return ""; + } + while (sk_PKCS7_num(encSafes) > 0) { + PKCS7* p7 = sk_PKCS7_pop(encSafes); + if (p7 == nullptr || sk_PKCS7_push(safes, p7) == 0) { + PKCS7_free(p7); + sk_PKCS7_pop_free(safes, PKCS7_free); + sk_PKCS7_pop_free(encSafes, PKCS7_free); + return ""; + } + } + sk_PKCS7_free(encSafes); + + PKCS12* p12 = PKCS12_init(NID_pkcs7_data); + if (p12 == nullptr) { + sk_PKCS7_pop_free(safes, PKCS7_free); + return ""; + } + if (PKCS12_pack_authsafes(p12, safes) != 1) { + sk_PKCS7_pop_free(safes, PKCS7_free); + PKCS12_free(p12); + return ""; + } + // PKCS12_pack_authsafes only encodes safes into p12; it does not take ownership. + sk_PKCS7_pop_free(safes, PKCS7_free); + + const bool written = writePkcs12File(p12, outPath); + PKCS12_free(p12); + (void)password; + return written ? outPath : ""; +} + +bool sslContextHasClientCertificate(SSL_CTX* ctx) { + if (ctx == nullptr) { + return false; + } + X509* cert = SSL_CTX_get0_certificate(ctx); + EVP_PKEY* key = SSL_CTX_get0_privatekey(ctx); + return cert != nullptr && key != nullptr; +} + +bool tlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs) { +#if defined(_WIN32) + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif + for (int attempt = 0; attempt < 3; ++attempt) { + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + if (ctx == nullptr) { + continue; + } + SSL* ssl = SSL_new(ctx); + if (ssl == nullptr) { + SSL_CTX_free(ctx); + continue; + } + if (RpcSslUtils::isTlcpProtocol(config.sslProtocol)) { + RpcSslUtils::enableNtlsOnSsl(ssl); + } + const std::string target = host + ":" + std::to_string(port); + BIO* bio = BIO_new_connect(target.c_str()); + if (bio == nullptr) { + SSL_free(ssl); + SSL_CTX_free(ctx); + continue; + } + BIO_set_conn_hostname(bio, host.c_str()); + if (BIO_do_connect(bio) <= 0) { + BIO_free_all(bio); + SSL_free(ssl); + SSL_CTX_free(ctx); + continue; + } + SSL_set_bio(ssl, bio, bio); + const int rc = SSL_connect(ssl); + const bool ok = rc == 1; + if (ok) { + SSL_shutdown(ssl); + } + SSL_free(ssl); + SSL_CTX_free(ctx); + if (ok) { +#if defined(_WIN32) + WSACleanup(); +#endif + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } +#if defined(_WIN32) + WSACleanup(); +#endif + (void)timeoutMs; + return false; +} + +bool thriftTlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs) { + (void)timeoutMs; + for (int attempt = 0; attempt < 3; ++attempt) { + try { + auto factory = RpcSslUtils::createSslSocketFactory(config); + std::shared_ptr socket = + factory->createSocket(host, port); + socket->open(); + socket->close(); + return true; + } catch (const apache::thrift::transport::TTransportException&) { + // Server may still be starting; retry below. + } catch (const IoTDBException&) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + return false; +} + +int findFreeTcpPort() { +#if defined(_WIN32) + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + return 0; + } +#endif + const int fd = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + if (fd < 0) { +#if defined(_WIN32) + WSACleanup(); +#endif + return 0; + } + sockaddr_in addr {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + if (bind(fd, reinterpret_cast(&addr), sizeof(addr)) != 0) { +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return 0; + } + socklen_t len = sizeof(addr); + if (getsockname(fd, reinterpret_cast(&addr), &len) != 0) { +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return 0; + } + const int port = ntohs(addr.sin_port); +#if defined(_WIN32) + closesocket(fd); + WSACleanup(); +#else + close(fd); +#endif + return port; +} + +OpenSslServerProcess::OpenSslServerProcess() = default; + +OpenSslServerProcess::~OpenSslServerProcess() { + stop(); +} + +bool OpenSslServerProcess::start(const std::vector& args) { + stop(); + port_ = findFreeTcpPort(); + if (port_ <= 0) { + return false; + } + + const std::string portArg = std::to_string(port_); + const std::string exe = opensslExecutable(); + std::vector argStorage; + argStorage.reserve(args.size() + 5); + argStorage.push_back(exe); + argStorage.emplace_back("s_server"); + argStorage.emplace_back("-accept"); + argStorage.emplace_back(portArg); + for (const std::string& arg : args) { + argStorage.push_back(arg); + } + argStorage.emplace_back("-quiet"); + + std::vector argv; + argv.reserve(argStorage.size() + 1); + for (const std::string& arg : argStorage) { + argv.push_back(arg.c_str()); + } + argv.push_back(nullptr); + +#if defined(_WIN32) + std::string cmdline = quoteArg(exe); + for (size_t i = 1; i < argStorage.size(); ++i) { + cmdline.push_back(' '); + cmdline.append(quoteArg(argStorage[i])); + } + std::vector mutableCmdline(cmdline.begin(), cmdline.end()); + mutableCmdline.push_back('\0'); + + STARTUPINFOA si {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + PROCESS_INFORMATION pi {}; + if (!CreateProcessA(nullptr, mutableCmdline.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, + nullptr, nullptr, &si, &pi)) { + return false; + } + processHandle_ = pi.hProcess; + processId_ = pi.dwProcessId; + CloseHandle(pi.hThread); +#else + std::vector execArgv; + execArgv.reserve(argStorage.size() + 1); + for (std::string& arg : argStorage) { + execArgv.push_back(const_cast(arg.c_str())); + } + execArgv.push_back(nullptr); + const pid_t pid = fork(); + if (pid < 0) { + return false; + } + if (pid == 0) { + prependOpenSslRuntimeToLdLibraryPath(); + execv(exe.c_str(), execArgv.data()); + _exit(127); + } + childPid_ = pid; +#endif + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + return running(); +} + +void OpenSslServerProcess::stop() { +#if defined(_WIN32) + if (processHandle_ != nullptr) { + TerminateProcess(static_cast(processHandle_), 0); + WaitForSingleObject(static_cast(processHandle_), 2000); + CloseHandle(static_cast(processHandle_)); + processHandle_ = nullptr; + processId_ = 0; + } +#else + if (childPid_ > 0) { + kill(childPid_, SIGTERM); + waitpid(childPid_, nullptr, 0); + childPid_ = -1; + } +#endif + port_ = 0; +} + +bool OpenSslServerProcess::running() const { +#if defined(_WIN32) + if (processHandle_ == nullptr) { + return false; + } + DWORD code = STILL_ACTIVE; + if (!GetExitCodeProcess(static_cast(processHandle_), &code)) { + return false; + } + return code == STILL_ACTIVE; +#else + if (childPid_ <= 0) { + return false; + } + int status = 0; + const pid_t rc = waitpid(childPid_, &status, WNOHANG); + return rc == 0; +#endif +} + +int OpenSslServerProcess::port() const { + return port_; +} + +#else // WITH_SSL + +std::string fixturesRoot() { + return firstExistingRoot(); +} + +std::string tlsFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tls"), name); +} + +std::string tlcpFixture(const std::string& name) { + return joinPath(joinPath(fixturesRoot(), "tlcp"), name); +} + +std::string buildTlcpDualKeyStoreP12() { + return ""; +} + +int findFreeTcpPort() { + return 0; +} + +OpenSslServerProcess::OpenSslServerProcess() = default; +OpenSslServerProcess::~OpenSslServerProcess() = default; +bool OpenSslServerProcess::start(const std::vector&) { + return false; +} +void OpenSslServerProcess::stop() {} +bool OpenSslServerProcess::running() const { + return false; +} +int OpenSslServerProcess::port() const { + return 0; +} + +#endif // WITH_SSL + +} // namespace ssltest diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h new file mode 100644 index 000000000000..06638ef60a09 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.h @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_SSL_TEST_FIXTURES_H +#define IOTDB_SSL_TEST_FIXTURES_H + +#include +#include +#include +#include + +namespace ssltest { + +constexpr const char* kStorePassword = "thrift"; + +/** Root directory containing tls/ and tlcp/ fixture subfolders. */ +std::string fixturesRoot(); + +std::string tlsFixture(const std::string& name); +std::string tlcpFixture(const std::string& name); + +/** Build a TLCP dual-cert PKCS12 key store from PEM fixtures (sign + enc). */ +std::string buildTlcpDualKeyStoreP12(); + +#if WITH_SSL +#include + +bool sslContextHasClientCertificate(SSL_CTX* ctx); + +bool tlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs = 5000); +bool thriftTlsHandshakeWithSslConfig(const SslConfig& config, const std::string& host, int port, + int timeoutMs = 5000); +#endif + +/** Spawn bundled Tongsuo openssl s_server for integration-style handshake tests. */ +class OpenSslServerProcess { +public: + OpenSslServerProcess(); + ~OpenSslServerProcess(); + + OpenSslServerProcess(const OpenSslServerProcess&) = delete; + OpenSslServerProcess& operator=(const OpenSslServerProcess&) = delete; + + bool start(const std::vector& args); + void stop(); + bool running() const; + int port() const; + +private: +#if defined(_WIN32) + void* processHandle_ = nullptr; + unsigned long processId_ = 0; +#else + int childPid_ = -1; +#endif + int port_ = 0; +}; + +int findFreeTcpPort(); + +} // namespace ssltest + +#endif // IOTDB_SSL_TEST_FIXTURES_H diff --git a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp index 4a298dd1c1a1..0eb9512f0a1c 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp @@ -19,6 +19,7 @@ #include "catch.hpp" #include "SessionC.h" +#include "SessionC.h" #include #include #include @@ -245,8 +246,10 @@ TEST_CASE("C API Table - Multi-node table session", "[c_table_multiNode][c_table CTableSession* localSession = ts_table_session_new_multi_node(urls, 1, "root", "root", ""); REQUIRE(localSession != nullptr); - TsStatus status = - ts_table_session_execute_non_query(localSession, "DROP DATABASE IF EXISTS c_db5"); + TsStatus status = ts_table_session_open(localSession); + REQUIRE(status == TS_OK); + + status = ts_table_session_execute_non_query(localSession, "DROP DATABASE IF EXISTS c_db5"); REQUIRE(status == TS_OK); ts_table_session_execute_non_query(localSession, "CREATE DATABASE c_db5"); diff --git a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp index 3b19f2e2b25d..52c34bd73416 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp @@ -91,7 +91,7 @@ TEST_CASE("Test Session constructor with nodeUrls", "[SessionInitAndOperate]") { std::vector nodeUrls = {"127.0.0.1:6667"}; std::shared_ptr localSession = std::make_shared(nodeUrls, "root", "root"); - localSession->open(); + localSession->open(false); if (!localSession->checkTimeseriesExists("root.test.d1.s1")) { localSession->createTimeseries("root.test.d1.s1", TSDataType::INT64, TSEncoding::RLE, CompressionType::SNAPPY); @@ -106,9 +106,9 @@ TEST_CASE("Test Session builder with nodeUrls", "[SessionBuilderInit]") { std::vector nodeUrls = {"127.0.0.1:6667"}; auto builder = std::unique_ptr(new SessionBuilder()); - std::shared_ptr session = std::shared_ptr( - builder->username("root")->password("root")->nodeUrls(nodeUrls)->build()); - session->open(); + builder->username("root")->password("root")->nodeUrls(nodeUrls); + std::shared_ptr session = builder->build(); + session->open(false); if (!session->checkTimeseriesExists("root.test.d1.s1")) { session->createTimeseries("root.test.d1.s1", TSDataType::INT64, TSEncoding::RLE, CompressionType::SNAPPY); @@ -386,9 +386,9 @@ TEST_CASE("Tablet index bounds", "[tabletBounds]") { TEST_CASE("Session rejects SQL after close", "[sessionClose]") { CaseReporter cr("sessionClose"); SessionBuilder builder; - auto localSession = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); - localSession->open(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + auto localSession = builder.build(); + localSession->open(false); localSession->close(); REQUIRE_THROWS_AS(localSession->executeNonQueryStatement("show databases"), IoTDBConnectionException); @@ -933,13 +933,13 @@ TEST_CASE("Numeric column widening getters align with Java TsFile", "[column]") } TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool]") { CaseReporter cr("SessionPool basic"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(3) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(3); + auto pool = poolBuilder.build(); { PooledSession s = pool->getSession(); @@ -975,13 +975,13 @@ TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool] TEST_CASE("SessionPool is safe under concurrent writers", "[sessionPool]") { CaseReporter cr("SessionPool concurrency"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(4) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(4); + auto pool = poolBuilder.build(); { PooledSession s = pool->getSession(); @@ -1040,14 +1040,14 @@ TEST_CASE("SessionPool is safe under concurrent writers", "[sessionPool]") { TEST_CASE("SessionPool getSession times out when exhausted", "[sessionPool]") { CaseReporter cr("SessionPool exhaustion timeout"); - auto pool = SessionPoolBuilder() - .host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(1) - ->waitToGetSessionTimeoutMs(200) - ->build(); + SessionPoolBuilder poolBuilder; + poolBuilder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->maxSize(1) + ->waitToGetSessionTimeoutMs(200); + auto pool = poolBuilder.build(); PooledSession held = pool->getSession(); REQUIRE(static_cast(held)); diff --git a/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp index 9ed3d334b1c4..f3c5aaf32c49 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionRelationalIT.cpp @@ -74,8 +74,8 @@ TEST_CASE("Test TableSession builder with nodeUrls", "[SessionBuilderInit]") { std::vector nodeUrls = {"127.0.0.1:6667"}; auto builder = std::unique_ptr(new TableSessionBuilder()); - std::shared_ptr session = std::shared_ptr( - builder->username("root")->password("root")->nodeUrls(nodeUrls)->build()); + builder->username("root")->password("root")->nodeUrls(nodeUrls); + std::shared_ptr session = builder->build(); session->open(); session->executeNonQueryStatement("DROP DATABASE IF EXISTS db1"); @@ -90,8 +90,8 @@ TEST_CASE("TableSession rejects SQL after close", "[tableSessionClose]") { CaseReporter cr("tableSessionClose"); TableSessionBuilder builder; - auto localSession = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root"); + auto localSession = builder.build(); localSession->open(); localSession->close(); diff --git a/iotdb-client/client-cpp/test/fixtures/.gitignore b/iotdb-client/client-cpp/test/fixtures/.gitignore new file mode 100644 index 000000000000..425d89e3c2dc --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/.gitignore @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +*.csr +*.srl +ca.key +tlcp-client-dual.p12 +_gen/ diff --git a/iotdb-client/client-cpp/test/fixtures/README.md b/iotdb-client/client-cpp/test/fixtures/README.md new file mode 100644 index 000000000000..6cd6b14a03f9 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/README.md @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Self-signed TLS/TLCP test certificates for C++ client SSL unit tests only. +# Password for all PKCS12 files: thrift +# +# Regenerate with: +# test/fixtures/generate_fixtures.cmd (Windows) +# test/fixtures/generate_fixtures.sh (Linux/macOS) diff --git a/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd new file mode 100644 index 000000000000..713ace0a2c4a --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/generate_fixtures.cmd @@ -0,0 +1,69 @@ +@echo off +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. +REM +REM Regenerate TLS/TLCP PKCS12 and PEM fixtures using the bundled Tongsuo openssl. +REM Usage (from client-cpp/test/fixtures, after cmake build): +REM generate_fixtures.cmd [path\to\openssl.exe] + +@echo off +setlocal enabledelayedexpansion + +set "SCRIPT_DIR=%~dp0" +set "TLS_DIR=%SCRIPT_DIR%tls" +set "TLCP_DIR=%SCRIPT_DIR%tlcp" +set "OPENSSL=%~1" +if "%OPENSSL%"=="" set "OPENSSL=..\..\target\build\_deps\tongsuo\install\bin\openssl.exe" +if not exist "%OPENSSL%" ( + echo OpenSSL executable not found: %OPENSSL% + exit /b 1 +) + +set "PASS=thrift" +mkdir "%TLS_DIR%" 2>nul +mkdir "%TLCP_DIR%" 2>nul + +echo [fixtures] generating TLS RSA fixtures... +"%OPENSSL%" genrsa -out "%TLS_DIR%\ca.key" 2048 +"%OPENSSL%" req -new -x509 -days 3650 -key "%TLS_DIR%\ca.key" -out "%TLS_DIR%\ca.crt" -subj "/CN=IoTDB Test CA" +"%OPENSSL%" genrsa -out "%TLS_DIR%\server.key" 2048 +"%OPENSSL%" req -new -key "%TLS_DIR%\server.key" -out "%TLS_DIR%\server.csr" -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" +"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\server.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\server.crt" -copy_extensions copy +"%OPENSSL%" genrsa -out "%TLS_DIR%\client.key" 2048 +"%OPENSSL%" req -new -key "%TLS_DIR%\client.key" -out "%TLS_DIR%\client.csr" -subj "/CN=IoTDB Test Client" +"%OPENSSL%" x509 -req -days 3650 -in "%TLS_DIR%\client.csr" -CA "%TLS_DIR%\ca.crt" -CAkey "%TLS_DIR%\ca.key" -CAcreateserial -out "%TLS_DIR%\client.crt" +"%OPENSSL%" pkcs12 -export -nokeys -in "%TLS_DIR%\ca.crt" -out "%TLS_DIR%\tls-trust.p12" -password pass:%PASS% +"%OPENSSL%" pkcs12 -export -in "%TLS_DIR%\client.crt" -inkey "%TLS_DIR%\client.key" -out "%TLS_DIR%\tls-client.p12" -password pass:%PASS% -name client +"%OPENSSL%" pkcs12 -export -in "%TLS_DIR%\server.crt" -inkey "%TLS_DIR%\server.key" -out "%TLS_DIR%\tls-server.p12" -password pass:%PASS% -name server + +echo [fixtures] generating TLCP SM2 fixtures... +"%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\ca.key" +"%OPENSSL%" req -new -x509 -days 3650 -key "%TLCP_DIR%\ca.key" -out "%TLCP_DIR%\ca.crt" -subj "/CN=IoTDB TLCP CA" -sm3 +for %%R in (client server) do ( + "%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\%%R_sign.key" + "%OPENSSL%" req -new -key "%TLCP_DIR%\%%R_sign.key" -out "%TLCP_DIR%\%%R_sign.csr" -subj "/CN=%%R sign" -sm3 + "%OPENSSL%" x509 -req -days 3650 -in "%TLCP_DIR%\%%R_sign.csr" -CA "%TLCP_DIR%\ca.crt" -CAkey "%TLCP_DIR%\ca.key" -CAcreateserial -out "%TLCP_DIR%\%%R_sign.crt" -sm3 + "%OPENSSL%" ecparam -genkey -name SM2 -out "%TLCP_DIR%\%%R_enc.key" + "%OPENSSL%" req -new -key "%TLCP_DIR%\%%R_enc.key" -out "%TLCP_DIR%\%%R_enc.csr" -subj "/CN=%%R enc" -sm3 + "%OPENSSL%" x509 -req -days 3650 -in "%TLCP_DIR%\%%R_enc.csr" -CA "%TLCP_DIR%\ca.crt" -CAkey "%TLCP_DIR%\ca.key" -CAcreateserial -out "%TLCP_DIR%\%%R_enc.crt" -sm3 +) +"%OPENSSL%" pkcs12 -export -nokeys -in "%TLCP_DIR%\ca.crt" -out "%TLCP_DIR%\tlcp-trust.p12" -password pass:%PASS% +"%OPENSSL%" pkcs12 -export -in "%TLCP_DIR%\client_sign.crt" -inkey "%TLCP_DIR%\client_sign.key" -out "%TLCP_DIR%\tlcp-client-sign.p12" -password pass:%PASS% -name "client.sign" +"%OPENSSL%" pkcs12 -export -in "%TLCP_DIR%\client_enc.crt" -inkey "%TLCP_DIR%\client_enc.key" -out "%TLCP_DIR%\tlcp-client-enc.p12" -password pass:%PASS% -name "client.enc" + +del /q "%TLS_DIR%\*.csr" "%TLS_DIR%\*.srl" "%TLCP_DIR%\*.csr" "%TLCP_DIR%\*.srl" 2>nul +echo [fixtures] done. Password for all PKCS12 files: %PASS% diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt new file mode 100644 index 000000000000..07ade3a4ced4 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/ca.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhTCCASugAwIBAgIUKvwxT3ypjPE0o1Xm4uy26vAV7dEwCgYIKoEcz1UBg3Uw +GDEWMBQGA1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2 +MzAwMzM0MzFaMBgxFjAUBgNVBAMMDUlvVERCIFRMQ1AgQ0EwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAARTre1ea094xClkcp6tz88qakjD3QL3VGQK2OBHWEECG8+v +bCqYUsbcOdNshtjk8MZcpznViFQaS3K+3Bf7FQwzo1MwUTAdBgNVHQ4EFgQUtwWN +1oBD+b/DANRs2So52umc9WEwHwYDVR0jBBgwFoAUtwWN1oBD+b/DANRs2So52umc +9WEwDwYDVR0TAQH/BAUwAwEB/zAKBggqgRzPVQGDdQNIADBFAiEAh8/BGnVxwjuL +yDkaOK/J1IL1c8wIGx6TqW7Re25CkCkCIDtLgej8xmZI4I0nL9Er+YhN8FD4BwzK +qoK4jYsnVf1Z +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt new file mode 100644 index 000000000000..8b3b3a0cddcc --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBJzCBzgIUWlOhvwyTl25h6Y2n3L//h7bOyXQwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2MzAwMzM0 +MzFaMBUxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAASRqpdiAJcuzGV2xI7NveKK4e/NtlRfnYg4DViomBN4a1sMwYCoz+5hun9S +mlsp/46HmgsHdCfySrMpAapjombnMAoGCCqBHM9VAYN1A0gAMEUCIGjsv/DgrY85 +W0GSyaB0KpFkId0D/s8Vc5hETJw/anC5AiEAyWw7RfrgYrLsSrvyh1rC9xd17jsV +ASgtvkYznvtAuYM= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key new file mode 100644 index 000000000000..a6d75ee094b6 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_enc.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgpM8Gsh1RlMa7+mM7 +rBwXX+zmn3rLR9xrM5CDyXQKvv2hRANCAASRqpdiAJcuzGV2xI7NveKK4e/NtlRf +nYg4DViomBN4a1sMwYCoz+5hun9Smlsp/46HmgsHdCfySrMpAapjombn +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt new file mode 100644 index 000000000000..6d64c3cd1783 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBKDCBzwIUJ0vuKfRYbq5vlfVOrnG+BuyB9EIwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0zNjA2MzAwMzM0 +MzFaMBYxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAEte6Lr50Tithv28OXSI+yewqHNbyGl+5vrgJg93LHzD+wyODruv+bqBF6 +N1KinzdYJrPtQiQOqTR4Zmw32bWVAzAKBggqgRzPVQGDdQNIADBFAiAD7TwAMMdd +r5EmQrDN9v/UGCaQnLOhIL3hoTlgCqR5EwIhAOeOx24taX93GkXWBym//EdUqeJ+ +jPJtJVNVUG5kCtla +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key new file mode 100644 index 000000000000..15537f66dca4 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/client_sign.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQggl/d0rcpSnK+Dawf +D6bzcQvOp4DyCMikGDAeYwY/BnKhRANCAAS17ouvnROK2G/bw5dIj7J7Coc1vIaX +7m+uAmD3csfMP7DI4Ou6/5uoEXo3UqKfN1gms+1CJA6pNHhmbDfZtZUD +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt new file mode 100644 index 000000000000..7e52a1280caa --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBJzCBzgIUGQOTcoIIr50fdhDjoPyyTTrYga0wCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzJaFw0zNjA2MzAwMzM0 +MzJaMBUxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAARxp2OqYF3uklRiNg5Dz89V/EDsw3uPaXKxqETMs0Jv0AynA/OINtjY1IK2 +jq5eoSIOJKAYV7kIXg8xEAxF1dpQMAoGCCqBHM9VAYN1A0gAMEUCIQC5zmE3XgT6 +qlnFNhUhtk2gTsbC0D0iiVh7oGDHsdV31wIgG/xfFl46bsoXkRdrZTgrDyuSjQ9r +b+SE017fmwAVLUo= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key new file mode 100644 index 000000000000..0edfda1a4258 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_enc.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg8n5MGmIXrvuybXKW +T/xAMviJsTFOfV/ZSjhcpdrX6PehRANCAARxp2OqYF3uklRiNg5Dz89V/EDsw3uP +aXKxqETMs0Jv0AynA/OINtjY1IK2jq5eoSIOJKAYV7kIXg8xEAxF1dpQ +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt new file mode 100644 index 000000000000..a9176c0c62a8 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBJzCBzwIURRSH5ItGF6FHEy/mLryngsiS7AMwCgYIKoEcz1UBg3UwGDEWMBQG +A1UEAwwNSW9UREIgVExDUCBDQTAeFw0yNjA3MDMwMzM0MzJaFw0zNjA2MzAwMzM0 +MzJaMBYxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0CAQYIKoEcz1UB +gi0DQgAE7eWZ4hSOUnlLO1ZYHGiM+tkYmidfNEIgx/p3bAXB3aWl49WKbA8uMVjI +75QSXvYW1EqYThHRd3Zz2NU1NWBSDDAKBggqgRzPVQGDdQNHADBEAiBbMYyjibY0 +1mPxsDf1KemntnmhTaSukWpyDTu9bdWYKQIgKT9Rsri4T6eCGyeTtU+olCH5S38+ +PHYQ42imyJU6oHw= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key new file mode 100644 index 000000000000..9f546d2f1843 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/server_sign.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg9FrxcDNbCS5UxHuN +zF2fRsR5Xcn82MG1DxwgtcQzc6GhRANCAATt5ZniFI5SeUs7VlgcaIz62RiaJ180 +QiDH+ndsBcHdpaXj1YpsDy4xWMjvlBJe9hbUSphOEdF3dnPY1TU1YFIM +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-enc.p12 new file mode 100644 index 0000000000000000000000000000000000000000..fcbf1c7c1716732d7749a675ac8e64fb94215fc9 GIT binary patch literal 1029 zcmXqLVqs)rWHxAG-px>$@UU?}Y-8eNWiXIs<4kDt zU`%CZVbo#~IKO1BK@R`)H|pKu70cQ*^%|NOHyB&&X)s+dd5UNGti4GyF1~s3#XZBI zUOJ9Z$G)^|VVx0c>9=+FWFwTtCj1S)7jsK0U-4Ydll_)@cckx}I=A=Sr@D&zewS9O zo;0OuwZoGF<}K0L#oWg;v47Ls%+G%d&phD2rziC>VOLG^r(f>Zm`aY-DTJ=|tj*J7 zb`E1d@aC~G$EKP_uc=E4L$^P>vwru=&hI-CgV(6cKeqB;uJ#1quw*kM^M%K@H*b2h zu1ALTT&7ZyoXV6#H+aL+w+Dn7+xgC(TRO8~QjW;NCEShYTvEN(Z#MU;`M9n4i9vGQ z%U_J$JT5k?jiQ@=^FP^MaBYfJa-Y%RF1<4?&Zjp#@n*gK&WiO|!TbO#frwx3i(3~L zB&B;j=za5d@+a1Ba(j%pE7i39*KRMIelc>cM(wMIOeP_6ixgFuyOWx2mc7qi(mP?c zt+edUJK2fxIUg)vbh^~unv^wVWyYiFtzpt#U1s|g*pEf2e4X<5kXmkf_O&n{iC^8j zZ?>F%HZ9tFdB1Ezp3-iSy-Sy@sNY@Q9R6+5l^sv)4o`XPAO#Td@g#25?|_6K5bHg0HINs@-C4V=X=N)ETucqpnawU(~Nt0X(21pUl;9hc4@kLj6wQ2 zt3p-na>Jdb>FeEeNFk~{MGUPFo zFzA8lWMoC6hGHxtvz>M%C9EvF*d(xFb=(fwm>QSA8U~IAh6cRw48+99%D|%Vt|9d4 vK8Xay#PiE)k7VXOUv*~1q_ZpRuk`v&IT0@xQNhCT@22FvQ@U;Kpx^`mvyGR% literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-client-sign.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d82ebeba9be440a47a5efe98d73519098a4f97e2 GIT binary patch literal 1031 zcmXqLVqs=tWHxAG-pR(P)#lOmotKfFaX}OFDwZbZWkBIY22D&x2r0G&O-#x_A$cZ7 z1|TJYkYO|khjUnY4ZI9A5nLVv85WN3hI3b36kYv~iHU>3fQO9(VjB}DD}#Y78)rhB z2V*KT3!@f`z?lQW--NPqN{^pU*IYQcJ+YyQaYL)y#Inh!y?pKOe28Z=6rQmCm+Q~O-Zg|-rFUv2Rr*LG1;lSQSH&r zXG{}qpB`DRHb-IU7qJ;D1Dh&8yU*fZ*6IHC>cy_EuB?n-YQJm@1Hw9G<92R(yWw4{ zT+<1G52nS-6|4F={FeU-lS%BC>D_bx$}}CPZEHL>b}o8yM|PK;nzN2v3ExF~5k3E# zzm^M}JGuOO^yyPvAKId?DkiruH4ByfHB@-G`*8ld>rqx}Mw0Cc%XRI4?cE{&<>BTP zS8KTsJ!$x`dE@&%+2uS#3aa;bteg^fBe?@%}MZ4PNLLZogD|;rX{R12;-}} z#%dMe$+OxbHB0NL^6boB1-4~DhbQV6{1INt;aGP=_C$(s-)u%rw^H-oUmv7)PRlmE z+HuV<_})Sj?ew=7&zrAf%bL|EymaT|kZFrnDt+gB^=9cK`S!Z~Tv8mY%h$;}H;crU zIMtff^sh2%IPk-ZL&jusv(ou>Iue|#d?dDp8Q5Oq^q7N^dT`MUUF=%4UfX6>5K`}&$!)54%q1wcKzEiq5r?G%&$LlmxYnL%eLy_7q?R?N_uBDF>h}?S;%B_ z@L-ny{U32ko-=$NPLwRjkiN$Ja#`_hIU%!unQu;hWO!vGnmJ*y>ay(f-eT8_7n!&Q zG@mzK`A(=x-#A{Vihn&rdv^~jhx??haa>K7hCT+$@X+BDH56kJ@p^jZO4f%*QgVl8 zc78aHw743tS|Xp9l(20Lbc) AoB#j- literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 b/iotdb-client/client-cpp/test/fixtures/tlcp/tlcp-trust.p12 new file mode 100644 index 0000000000000000000000000000000000000000..e7322f292e799c0ffb1943b5d72abb1884ea8b57 GIT binary patch literal 683 zcmXqLVp`6`$ZXKW6wAh`)#lOmotKfFaX}N4A4?OH7f{#@h@B8pY(ObvppZTjBLk4q zK*%r}gu^+kyarwdng}kBfeZ_WmGo7mhWs@rn3y;i40zZ$Aht1avN9ORvT-J~c`&9j zvoLD02pBzgxnmg5vG@3g{ofY!uzNK$F&?mI&0c)7Ld(0>gXuE!d>zF(O((xTkJxrH zOxUI&`+8@T_*Em`Um7ut3;yqV&DhWLPxM#orAvah<`&OnxO?)}9T!&Lsq#JFpHAU= z`bfs_%?qB?_}8B*<0E-gk*plnrHOt)F`Ng!j!rFYUQG+`9e` z3wN!!&FC@n+@Uj*?{6{duiX%?v()ovs>Bb*sY`$GKRae|k@2SKEUZO7sVwKiv4| zdbHHR^JmL-z7;$#_0G(@Q7jgw9e(Ky$JQecT~D53a&-1ycaY0rq27!SGflr}m`T3M z>Pu!$Dr^q86P&+~;Zq;SfhWS2{A{nDO=S7HS1EK-$jL`8l~KN)OIa2yGz+WQQt#CzJWN*lf#I2srl@WP{+iIJ6oMd83Y@f|bQ tJl>gci+la4)4VUfy%cyH>VKe6t>e^N$w@luEF8_;$IfWGXT(uBP{>` literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tls/ca.crt b/iotdb-client/client-cpp/test/fixtures/tls/ca.crt new file mode 100644 index 000000000000..b356ec366329 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tls/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIUFNsUYuwzkISBNzd0RdZEMLRjhu8wDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNSW9UREIgVGVzdCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0z +NjA2MzAwMzM0MzFaMBgxFjAUBgNVBAMMDUlvVERCIFRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCutbmN5+3qN8hGPzIys3XH5sSTnBmXbGNO +MViLiE8kysCfRMlc4ckHri/EdTsgH+V6mjf0rxuyH2+TkE7kATiYSU+a6EB/N1Fv +hpkEi7pL8lProdtcyriTTE8PahjdbWnpTe8lNjQFbkhRnQaJr0R8DGEXpVdsAVez +gcG5lruj0lYzZRIWhVxvSEzKTUnqaO83NcEqaRobTLj2uCmfLo4jLd4OQGf3J94w +6ayhNfP7U4iQeReheI9YhDjNIgkClVKgmmiyQb0VfE+O/nL1OVOazybkEHXNA8So +mN8MRafKFCSm+T1t9MBHHbcXp1tZRUHN0x4RjmAU8MPZiGQ55OsBAgMBAAGjUzBR +MB0GA1UdDgQWBBQWSSihr7kDyU9GS6cQaPz5+vtwhTAfBgNVHSMEGDAWgBQWSSih +r7kDyU9GS6cQaPz5+vtwhTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCT2TqltOY8slGpF+wB+pBELa1vcnZqajju5OO00uOYvkbKBEZ87wSO4Rag +idRnKKeKPe3KcvT7DdR2z21hl36pt8neDC1b8OohL4quBcO21t7gUbmxvfcQLijd +V9wd9sm8TriDIO/mx8ZsDwhu/dupGobboqy0r+C9t5/GsjdL17Kp30st0KffKsSs +UxCaeL0sMD2tVQvx1a5BRrQl8IrLEzpVWDhVFiWY7iwimMX0c+bI2CcvWb8Hd/Km +0l8Wt84XAYecWTVMEBLc/T5kj3DGv8S6TkAP4AgMn5Zjv59va+Pt7CoXbxmdjO8T +agd3Xt8jfmKDNmnQkPmKTo5uuErR +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tls/client.crt b/iotdb-client/client-cpp/test/fixtures/tls/client.crt new file mode 100644 index 000000000000..2d3dbc14fda9 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tls/client.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuzCCAaMCFBWG7ViMzmyrCBoJYpbg0zkC6USVMA0GCSqGSIb3DQEBCwUAMBgx +FjAUBgNVBAMMDUlvVERCIFRlc3QgQ0EwHhcNMjYwNzAzMDMzNDMxWhcNMzYwNjMw +MDMzNDMxWjAcMRowGAYDVQQDDBFJb1REQiBUZXN0IENsaWVudDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANsjPpYWA5e0HKyUxbdoVtdYjtnJegHbHdz0 +I4hbvBDe5ySMdBIEUtNTb/zGmxb0nhkTjIxV9wh3Wb3JVhNJ4oaIclnIjfMWNc0/ +o+j8E+lce1VIV5CfZwiYUI6cilP7H4vkaGrTW14x0LcJgU8BhoQbzk5GzRdVcayc +h+nDIsTfbMoT6Ag7dq2mS32Iq0F58IFP9ELT8cJ9Ue1mfWE74d+O5P/NtPU2CWdZ +JXu4yka1Li8Ug3Jq+6I2LmDlBbiq+IjF5kj3iyDIBU34b2WdiOChuhaB4EyhiXf6 +j+nQM7D+N1CCf55AtfJKsiLtA3Dp73uL3OE7yr/e1scHxOG6SOECAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEAnCt5Ffs8FkKRq8SkFnqLgZX2M0mlfXe8SzQk+dFPX1s+ +/2A+6JkiZ9JniR22uryUt40B3Cq2U5zhsINVlR3voye1F8MjJxEtaIfPTTh8MI2L +vyAQaIKtBj/VJX+tCiaYyO0tSCrAyBvdzArGcwcr3V0SdPxLzT7q4DrDM9F0uf1x +dDUgn9inGDBpWXHNgnOLzqM7Xjzs4+vbZSCQBbYY9HTmyvp+NDFmTT8dKC2lvMZH +Cugw0tTHv2N+wXwx33LUtAPxO5WRCZQ8PhWxJ0lGtV9MMJK2YvNyf9qCHGMiOgX0 +w4q1Gwh6ZTF9Nhsk7gNtDit+bDLb06gtA6oeNI/d8A== +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tls/client.key b/iotdb-client/client-cpp/test/fixtures/tls/client.key new file mode 100644 index 000000000000..1686d990843d --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tls/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbIz6WFgOXtBys +lMW3aFbXWI7ZyXoB2x3c9COIW7wQ3uckjHQSBFLTU2/8xpsW9J4ZE4yMVfcId1m9 +yVYTSeKGiHJZyI3zFjXNP6Po/BPpXHtVSFeQn2cImFCOnIpT+x+L5Ghq01teMdC3 +CYFPAYaEG85ORs0XVXGsnIfpwyLE32zKE+gIO3atpkt9iKtBefCBT/RC0/HCfVHt +Zn1hO+HfjuT/zbT1NglnWSV7uMpGtS4vFINyavuiNi5g5QW4qviIxeZI94sgyAVN ++G9lnYjgoboWgeBMoYl3+o/p0DOw/jdQgn+eQLXySrIi7QNw6e97i9zhO8q/3tbH +B8ThukjhAgMBAAECggEAH3DoGuqfq1V5Q724vH7o7s7S+CZzLe79UuVob7kRu63v +pgvM34TlSVLQX4kzWVDRmjF22e+/mORe6N8JTY0tRjYvifg/faAzKfa2ksgQJ0xQ +mcTeY26rfs0zybJmGnSOayjjXmhi1Jn7Izfm6KoEXdILgKmh5XYp8CUpTv3jcDGG +MgdlqxG8rakJ4NHtO6qjgbaEAEsI7JbJj0T+7YPPD42KWvy9f9LYUSN0eCO/7TWw +Cvvl6NX55tz+WwpWdDtIKVjWZRnx4ZA3cZizWGZfSDaoRJWfDTcUKJ4zXHDdxXRL +6ha0cD7N6HJtQFvrxnyH/Uqpnm9rTKhfHvj5uTmaLQKBgQDdufi+60dIjVzQxYhy +4w+BUaI5PQCN0naX+uvlUBzlctrImWQyGsLj2yDbo9IuDBU6qaGPC0Sl29ywBf7W +QxsWbxE/rb9MKO2SEdLRq45W/H/Llr1IV494upWnDpWgvanBeanITveU9HA0/Fm4 +U0PrfExeBXca0dTfAD07Jr3gfwKBgQD9AtTKku/+jXfpQe4IPtZ8rjG6Ezg6KCw9 +JQVwHQaTked82Fj/1F6BiutVQwbQ6UI8FfZ7uF239Cw2O/PI28zpaCtOUPCP5TOI +A1LdwhJAtogfXK1vSX4qxog4sNwmlboxAMixdSZGuBfO/vUxL6nQb+OH3g3gTqS5 +CjnKAQcmnwKBgQCAkASbLvD2MIFQzDiB5QZohVz6s1RO52m8VdHR9NHMePxCtC5U +nw/B7pzuvd5wtLDaguEaf/4d7Y3YwqEwu1hJeb0Wnzf8gP6/Y3ZJ/J9b8Kxo785w +09RsvENpyhsYSODVPiYj7yW/SLyG/ItJRX5sXHYrTh/xfRlg9FKMqboPIQKBgQDz +0K2kxTKXOFbspocu1Pc20VrEOM8/ZAU1qx5xatcykDDmo0ooxsuHxIqB8IR5/76/ +Tl7n3MQbiCau4NlNn1r5NlQ9NUyNLk+Za7KIVwPl7sCAkHvluYnmyMju8KhGWpVB +scK1F/KZxb/TzugTzR206o32GWt/0+lzE8KawqDUewKBgCIc9mkWBtyA5Z7qKDmZ +6yaKs5210GzXGHBccVn6ABzV9BsWh+9r8guT2WxH6q+i4KBmFkRAk1u/AK7o/WOi +2HdOTMgQe5j9Jxnzr6sOQcSHJLYblKbkzGonJ0eEiZH2qFtjjPIz6lqSiuXK6PPr +fBH/Y8bZLE6KjrsBiqlzUiqV +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tls/server.crt b/iotdb-client/client-cpp/test/fixtures/tls/server.crt new file mode 100644 index 000000000000..5b36e382d13b --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tls/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGDCCAgCgAwIBAgIUIzsFiFOpqHEmojkqKN9uUGU4uw4wDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNSW9UREIgVGVzdCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0z +NjA2MzAwMzM0MzFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAJvzk6OA9O1zWJjPd0wOTqFcGz62XWxWDlFvUeoS +SHimZkSHyIUxduWgXvj5xKnH9372ZFN+K0twmaubDWLxfXyXBTPV+w8TXNoxVP3e +Ibj0Bora595egt/iEQCh3R095DFzK7nTFrnRWahojg4ZJViz1zREr2DLGB1Lz1aM +hfmUdExcjMP0MM0JIhhieQ7GoD353DUR38vpaEoTO+3KUDLxByfcm+bsRYCHA7OS +n0qYM4Zm20+RHFiu3ynSh4fRoWZ0OE+XfvG+buEX3vlWW7NcRRmsK3QhCmwB/TMn +DlTfmXIQzy5jL025GShHNvJRJV6bhAVVP4Zbg3ADlX4jf+UCAwEAAaNeMFwwGgYD +VR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBT6g7pH7yv5c5TjadgH +3JGeWyYQRDAfBgNVHSMEGDAWgBQWSSihr7kDyU9GS6cQaPz5+vtwhTANBgkqhkiG +9w0BAQsFAAOCAQEAq62942UsASWaOfbRuI4TYfXWDrOtmBnXzWldlnkiBomtph8c +CLdKPQYmY6/UuIh66/vaBQuyqvThWTBDZG5eEx33oxNvXBltMxxiiEf10o4RNpmz +WzNcN4kW058RJtiK4e3T3XOoTObVVEyOzM8nsORZH2ayuEe+KOQ66Gm/OBThSt/i +YgjbWnyaFbWEKIzkzPp/FqTnx7qJQ5Bm3pMD6pB3HI3CVXL14U13iK+B45bdF1E5 ++NbMVGuXkngNZQSGjQBMbYFBvlDJK7N7REdvBnshXgYCrDQQpe3fRpYgNA/RwkFs +w6/YiYe3AoFCUnXQfeViRUkGZfxJFhUFBAKjdQ== +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tls/server.key b/iotdb-client/client-cpp/test/fixtures/tls/server.key new file mode 100644 index 000000000000..cb3724767015 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tls/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb85OjgPTtc1iY +z3dMDk6hXBs+tl1sVg5Rb1HqEkh4pmZEh8iFMXbloF74+cSpx/d+9mRTfitLcJmr +mw1i8X18lwUz1fsPE1zaMVT93iG49AaK2ufeXoLf4hEAod0dPeQxcyu50xa50Vmo +aI4OGSVYs9c0RK9gyxgdS89WjIX5lHRMXIzD9DDNCSIYYnkOxqA9+dw1Ed/L6WhK +EzvtylAy8Qcn3Jvm7EWAhwOzkp9KmDOGZttPkRxYrt8p0oeH0aFmdDhPl37xvm7h +F975VluzXEUZrCt0IQpsAf0zJw5U35lyEM8uYy9NuRkoRzbyUSVem4QFVT+GW4Nw +A5V+I3/lAgMBAAECggEAOiyHc2OEehsf/ojNqJphrIGOTDt86A/F8YzEErvVOuRf +m8rG+yBziL7lDp0lRmwon7zLufWDsvWC7We+e503wUUYlLiKmZoQdfgXC0hUbgjh +c+Sqv+Gjkl5jF0hKEkFnISckNYJPpOs6Nb8i1pF8w/T6Hy5L+aBpE2yXIGL1Cx9H +fID+vTpYI3W0pQajQJr8RLVRUbVn9ZBNIwqteAxHlW7OlZF9SqrNYo64ANh4f7Pa +So+Roxx9bJWj7sbf97HEMEI9S2sdg03UTFlC6noEzSZbouTwURG1Jj0zlexpKqbG +QYc0D/NG+IEifAwv35jOhWjhUhz5PnZNyNX9IoJx1QKBgQDQNAClbbTEYTm/Mjn+ +iqxqfAMgOJJkRBTmmKnJxJDq/mTDw73E2udGwzHV7Ia2qTmmDChhWnPhiAL7YGPz +Ha5cZ/SvN8Mg+IgEGhiJtLtFd8Wa3NL/5YuHSMVRVcohIb5kNfPJ2grykceuhvj8 +eTfYGYtm/yFbjFK3iiEHOR9nhwKBgQC/wMTrzlM4ttn42F0wms1pkGU7Dy6nzhIy +uQwzgGyS9XZzXkFt5k9n7GF0x54UILCz4hUMbXPt48Vh/z9oYBvV++2bhthzpTdN +Bos7N6HT4fsp2lM/CLxBSDgY2iXzFYmZwmLkT5DpGXl8Kw2JFfpLSdLF++1MKjVn +LixMGscgMwKBgFR59RUqKSFRDZwtJejInWJrRN1q9sLl+NEDekiaj+45H+tqXXIl +G5fTlUHmQVaV3QUpg6zUhZYpmIQkPQmkrl1h9J6vcmXLUWzQpcoh4aYzDaNjG92m +ZnGSrjTtKSE+TsDcPzlUVgLL8Yg3zYirKmRtIOm+dOtvSRSdX/9NRxe7AoGBAJpA +iCjpISlObqov40dmQDfbYJPR8sYqj5keIyKK/Mx7iX3lJN0zmE0RapR2wEOQyJTs +GeKVadzEjdP3cRqVtc69irbCEv10urfLu9U3O4cnEtToPG9Ip6gcYIQdeMnDWZ3H +MaZYG0poo8GvIIRWmbJXAKcjshDDI5KNIjVWlTjhAoGABmQYrlSDsdCuj4w81NBL +GDuhEiHEUYWAZjut1oeaoW3fN0FRX0mN0/MupJFY9GumduD1cE5nEAfoWcXmVOmv +Qup6kAIne0ulO7AlyM6OBjMQm9U52PuFkyZYKXppQLXcU5AXHNvjS6ychrvtPsBJ +6CyxH51/2GhMXFx5Gr7nIao= +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-client.p12 b/iotdb-client/client-cpp/test/fixtures/tls/tls-client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..063272846dd1543c170a8c17e0f9e910fd1df5df GIT binary patch literal 2512 zcmai$XE+-Q8-^2t#NJ}GQlnaf#qv_@Oen#sorwN<21(H6DUtQoBsY3)s| z*sCZ_TO9~erzJ*|!{=PzceOvykN0}7=Xrj-zwYOH;RMJq8;~7NfDkw#%0yG*4j+&U zSU`Z>1rZ>B9@!W;fxYW@#92UKuQ{?PY(T)#dht5}!ms}l9DHzd`03x07cK(=AD;r% zy#|PMvax{ya9&RE|E{q?H~{ctoDdYz6zIVY0xE+9o9SW83lSn`LzqSeJVgl$cmlh@ z+yEDuoFWw}YWtoS1|ejxJ`J*d%30H~VcB!54g5}m*`8R8SqS1?jJx$0Do&vKn=1sVDd*GK9!~Tl&L7kx1kDh=R~R zzI0wt)?VuLFa5B7tSIWMsF5`aa1xp`EiBP-$CK9OgKlPGxCfJ$z|BoQ^*SCW%m6u; zXyFt22Im$qta3EITYe8EaKjY;Dg z9Nb5=Yj{a$sjHE zT3#%s_2|Z_I(nO)-F~HVd}gQnN3ihHe@s|P;Ny~Q5HXa5)_3VK3y_)H>%)!y(8+3Kte=hZdW9oLs!scr(ptfRmn2 zzx)|_@tb)c8GZFrC;oJ<3{M?r$Kt&Gd3L04Sm{q3U3#Uz*yuC)3gJyZMGU3R^ph%ufzg-Fvww%g?!*Z3pF(McX9k6051PelA(Q-3wTafBPFF30qATHf*xGQe~$W?w59v9J__oX zoq9VFA<%8Sx|Dk=37URm>(V``sY^SdZb(N3OT|7f6u*GgbiM3=&4CYRPgJj3pnFWH z#F>RC*4Xp|ki1vbJ1yNdEKRRvY#xyh zCA(KCMb+~9k}tqT3ddc+su!+P2EX;%9be>Q(tG0jz?na^`bk!W=sn=Def;xR=N`>) z)Jr6qWJj*L>~*81=!OBLrSsW8Wh}wd0y)WC;#xsj{;x0&2`F2AsD!+qcbt&e+ENWl zY&LO)a5q*^+xJi*AVJ16M19IGiMujzMT!SSFpYJqjto_bw#ni$^;J&IkZ3qC@-Y{T zRz^}gmsrlC3q3{8s3o@zFRS#`&;k={wMJP%&kk~S-zT&L*7C+WM*D$JPteiRc{V$XVvX^6Qwxi*K-pQ~!L50hV5&u`9nX6>b8MhYmlj@nt6 z#e0kN3r)XxTuAHppj^B6ETbyPilk4LMzBdqHTJ0%*is41Q!gs6O%rmixW*c4FUyZ@Xx+w1n5zUv9difX9O&&R&z&@h5a|0-CB zs0ROWUceo|e*iuJ4}c#a5H9~a6w?p~iH;TcG@cVVT-Y-)&K+L4B6eWFW(zljYry$_ z{X%R&4gd(&@#;IbH(9Moc55Q_T8E&IxYS~0mBOX)dXk!>8{H)g1isCUU?-x)e*DwJ F{{;a&i5UO@ literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 b/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 new file mode 100644 index 0000000000000000000000000000000000000000..53ac363590cc0394ac234bc559c4d1ed67d69600 GIT binary patch literal 2608 zcmai$S5(sp6U9mRCxjlVl+Z=G29%{&fk=R*1O*8(bU}&~DPkx_DWMmoNC!owM~Xx# z7NmDDpon6mBLbp;Kw#PPeMcYnVP?*~_hFvqcLqshQ3Qh+kwj)1lvzIZTjjt1;c0z%sT77%tM8Y%ZjvLO)wEVS9k0BeB{ z27_S?NH!?!f7`&!5C)_Ol-Viv9LSv!0LcSzvmJQG+G}LFS^>{mJF((cG?B5`wG@1X-u^7<7v@XF;kTXW_p3ZWFarN5G+ae*6=mH(Mtj* zSk?WC&JUa=l5Th}+bl;5gif7+a`Pp7+Jf$rlE`=|#97O^H52dpvF!>6d;o58@gm;< zD_uJVx2$|fQq3QyN@j{qLJ~{_>p2`75sS@qW2G(rq>;bFims2xI9|ZNdC_!*;=z%oJ7S%XW1L1P6Jp z4_&fhWvMk*&^dk{r{2FmSL?fry^J==TCx(iD?4MUv6oSNh-;hSQ%gOr)<#lE`D@ErX^Hv&ddqaS&p594QWQNok;W)+caC)vf2gAPmBb`{8q+zyx~Hq*+GRTOmf)?)qG&>50I~ znY~h}Hm|4SfP!8m$d8W>CIvvPX0!6 zG^c%vA`P)${s?q3Kbs)NnPIp)|4R8mc6j@m@&1N-JO@&jn|?!Y(lswyvKoN4I0svQ*Iw)pQ{<5i4M5+z-K+;F(tTGH=B+HAQ=D= zl5%7x991lo_5W;PV*!DWLggb<>A!%{W@4~yq|DCqpTIyHMZLRKMdW;rmeIZ1MIr-4 zU^wcAI(IgEUg+l}DIdXyoR}Z*9cV>TfENC7$d-W54;zlL7BPyR0mfnI-kw6ASXN3M z`8Ba~WV2J72dnv*{af$Sj_ZU3ylyEJ1Nxy&BIqSMr<2agIS@bZWq;B2q{tvpL}#9C z?xz;;Uk`*8;Yuqn%+Hah(FH8So7ubDd3Qd}eC7r zL5VvlSAxO*y-M6ZSp;*ArRU(X4Y^r{8*QlN$KhRPCWy+B@J{&_5=+;bRiA3^Fdv<` zMyXcOVSp^Me#DupdJfMU`eR z66ZL$$~~Z(pSMETP}F4^M$#=mZcNUqyZJPWI6>4l;Tm_~TPyPJw^h^&j^SztV>3>` zT<*htb$z*ycFe&jFSv~e`lbTUx_nbHtxAqx_tw;GER}zC;O9UVD4lnBWKPtyVvPO# zw!%*TbkK_Nhi*GG$9#8H$URcvnZyUE;IH`nF3Ov1!Ixj#edi-4{jC7$;-U*t7S$6b z`#a&sIH%P~qhV6x$kwUa;OUNJ=fC{JxwZ>gVJ^nc^u1vgpIBQesKiH2NgEt#IhtOS zh!$x^DPwQTi?lhj?5-HmN!p3qiCOh!E~!{w&85qr-nwZ0=G>FI;=S=5NaAt3sg`7> z942y1Zpep5b8K8%SZ|zuq-7(az9nv>xBK>@oJ@nNij7 zD#Q7j>&ts4!qA3u8=KZOd#OT<2=`a+$D7|xxQ*wgvG<86%+pMeIx7Y*&~H`ta%f_v zR=i0rNJpsY&HfFzu}b|S8e#8FgZ>KP9$|CPmQ%YzBk)LXH9gS3+=K}bvkw(k&_?xj zN4BRBihRvwbRl0ui`63iCX_JpZtKGsYuGZyqiD;E%hFQr2M6Au+A^{c^u$|pD;j6b zNDnCFqN_W&op#MJ5>Nj`qYk*fIS_)ExkfuabLh(Rou3mJiEfm->F#IPA^W*Fs(3i> zGu_p{0RmX!qYla{3)ZSQOba~0k*>my2{HI7;a1kd#J_2_>AX*-4P=jVcp?sLPjD}z z%Ja9LO-dckE!}z0t_tJza+n5(_MwpdrojlSuXFgI$LVJy&}B;~)WTjO`{Sr%VqLh0 zSM(s?7s6K)eU?*et@O?cJf(~rQ*<)Qa}h=J>`vN@3pmMC z6tY^GL<=jJ+;e2tV3w(-3V}3xBc5bp4;`kVl=>6$Df0uof^2eVVrs-|>w2dQk8~d+C~L1R(g3N9WdHqZfk6-kK%C)3lU8GI rT?Ht2nY|yv#U_&GvUq6d*l@M(O3`V`Aqs$9n^-3r2DEJa%i(_jSlpnK literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 b/iotdb-client/client-cpp/test/fixtures/tls/tls-trust.p12 new file mode 100644 index 0000000000000000000000000000000000000000..f672208e9d76f50609243636d28972812886d5eb GIT binary patch literal 1083 zcmV-B1jPF=f&@1L0Ru3C1MLP0Duzgg_YDCD0ic2d-UNaJ+Ax9x)-Zwt(gq1ChDe6@ z4FLxRpn?O#FoFZW0s#Opf&;h)2`Yw2hW8Bt2LUiw1_>&LNQU zlu8J(%c$??yQ!rFgl-iWc;?a?P((A&jE@XW z$7ScN)l31m*80aOc7GUr+-$YWa%=G5c5X%fqQGw|mp=~zf{l6kR+p|uMN+O1+j|%* z`3V2R#A=AWpnH_8j2oV$t{KlP;(rGoK_OSrW$C`{Nn6yinydWF8};g4$~=O9asG*V z1)%w<*guAQK|n<79Uow^$4~I|JRHS{`d|&uCchp~iK<6IWyB^%I)oTyw!g=I;9i-Q;T$!|8@51+# z0!Q@HhNpdsgfx6mDCheB8wmwAFMnGrm=)KaN#{e7_4e>C>&>4So)HctGw0}t1LBui z(WB8Z7Hq+52rgdDLdC_k>xvYvbp9TFc|5iHT=0;LD~+#(FQPBLxN%y%l!4m{Y;#19Y5`ME+u!G6ZTs|F=?08hN@26!^bP_?-umisvW}%!HvQW_9PAN z{qf}*Mh#B!PSr`4b2?9nhSz67F@C<>KCv!Uo`}}WI*lAjUzLecZg+@1LEi|rsFBpZ zg#d4+t(Z;L)4jTTl4H4y1w)(}`=K=V_&i-#?{n(?`rwgS0QopHS|K`=2e4F(BdhDZTr0|WvA z1povfCk1PGig9Q&2v2igJx2mp%S B^<@A6 literal 0 HcmV?d00001 diff --git a/iotdb-client/client-cpp/test/main.cpp b/iotdb-client/client-cpp/test/main.cpp index 1bc3425882fa..b77006ecd429 100644 --- a/iotdb-client/client-cpp/test/main.cpp +++ b/iotdb-client/client-cpp/test/main.cpp @@ -32,12 +32,8 @@ struct SessionListener : Catch::TestEventListenerBase { void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { if (!session) { SessionBuilder builder; - session = builder.host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->useSSL(false) - ->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + session = builder.build(); } else { session->open(false); } diff --git a/iotdb-client/client-cpp/test/main_Relational.cpp b/iotdb-client/client-cpp/test/main_Relational.cpp index de808c23224e..dcb045e25fe6 100644 --- a/iotdb-client/client-cpp/test/main_Relational.cpp +++ b/iotdb-client/client-cpp/test/main_Relational.cpp @@ -30,8 +30,8 @@ struct SessionListener : Catch::TestEventListenerBase { void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { if (!session) { TableSessionBuilder builder; - session = - builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); + builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->useSSL(false); + session = builder.build(); } else { session->open(); } diff --git a/iotdb-client/client-cpp/test/main_rpc_ntls.cpp b/iotdb-client/client-cpp/test/main_rpc_ntls.cpp new file mode 100644 index 000000000000..ec0bec9adb32 --- /dev/null +++ b/iotdb-client/client-cpp/test/main_rpc_ntls.cpp @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define CATCH_CONFIG_MAIN +#include diff --git a/iotdb-client/client-cpp/test/main_rpc_ssl.cpp b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp new file mode 100644 index 000000000000..ec0bec9adb32 --- /dev/null +++ b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define CATCH_CONFIG_MAIN +#include diff --git a/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py new file mode 100644 index 000000000000..79c5a19de111 --- /dev/null +++ b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Patch IoTDB distribution RPC SSL settings for C++ integration tests.""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +import time +from pathlib import Path + +STORE_PASSWORD = "thrift" +SERVER_PKCS12 = "tls-server.p12" + + +def replace_property(text: str, key: str, value: str) -> str: + pattern = re.compile(rf"^{re.escape(key)}=.*$", re.MULTILINE) + replacement = f"{key}={value}" + if pattern.search(text): + return pattern.sub(replacement, text, count=1) + return text.rstrip() + "\n" + replacement + "\n" + + +def stop_iotdb(dist_root: Path) -> None: + if sys.platform == "win32": + stop_script = dist_root / "sbin" / "windows" / "stop-standalone.bat" + else: + stop_script = dist_root / "sbin" / "stop-standalone.sh" + if not stop_script.is_file(): + print(f"stop script not found, skip stop: {stop_script}", file=sys.stderr) + return + print(f"Stopping IoTDB via {stop_script}") + subprocess.run([str(stop_script)], cwd=str(dist_root), check=False, shell=True) + time.sleep(15) + + +def configure_plain(dist_root: Path) -> int: + props_path = dist_root / "conf" / "iotdb-system.properties" + if not props_path.is_file(): + print(f"iotdb-system.properties not found: {props_path}", file=sys.stderr) + return 1 + + text = props_path.read_text(encoding="utf-8") + text = replace_property(text, "enable_thrift_ssl", "false") + text = replace_property(text, "thrift_ssl_client_auth", "false") + text = replace_property(text, "key_store_path", "") + text = replace_property(text, "key_store_pwd", "") + text = replace_property(text, "trust_store_path", "") + text = replace_property(text, "trust_store_pwd", "") + text = replace_property(text, "ssl_protocol", "TLS") + props_path.write_text(text, encoding="utf-8", newline="\n") + print(f"Configured plain RPC in {props_path}") + return 0 + + +def configure_tls(dist_root: Path, fixtures_root: Path) -> int: + props_path = dist_root / "conf" / "iotdb-system.properties" + if not props_path.is_file(): + print(f"iotdb-system.properties not found: {props_path}", file=sys.stderr) + return 1 + + ssl_dir = dist_root / "conf" / "cpp-ssl-it" + ssl_dir.mkdir(parents=True, exist_ok=True) + source = fixtures_root / "tls" / SERVER_PKCS12 + if not source.is_file(): + print(f"fixture missing: {source}", file=sys.stderr) + return 1 + shutil.copy2(source, ssl_dir / SERVER_PKCS12) + + key_store = (ssl_dir / SERVER_PKCS12).as_posix() + + text = props_path.read_text(encoding="utf-8") + text = replace_property(text, "enable_thrift_ssl", "true") + text = replace_property(text, "thrift_ssl_client_auth", "false") + text = replace_property(text, "key_store_path", key_store) + text = replace_property(text, "key_store_pwd", STORE_PASSWORD) + text = replace_property(text, "trust_store_path", "") + text = replace_property(text, "trust_store_pwd", "") + text = replace_property(text, "ssl_protocol", "TLS") + props_path.write_text(text, encoding="utf-8", newline="\n") + print(f"Configured TLS IT server properties in {props_path}") + return 0 + + +def main() -> int: + if len(sys.argv) < 3: + print( + "usage: configure_iotdb_ssl_it.py [enable|disable]", + file=sys.stderr, + ) + return 2 + + dist_root = Path(sys.argv[1]).resolve() + fixtures_root = Path(sys.argv[2]).resolve() + mode = sys.argv[3].lower() if len(sys.argv) >= 4 else "enable" + + if mode in ("disable", "plain", "off"): + stop_iotdb(dist_root) + return configure_plain(dist_root) + + if mode in ("enable", "tls", "on"): + stop_iotdb(dist_root) + return configure_tls(dist_root, fixtures_root) + + print(f"unknown mode: {mode}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py new file mode 100644 index 000000000000..3aa7d9d3ce0c --- /dev/null +++ b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Run C++ client integration tests in two IoTDB modes: plain then TLS.""" + +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from pathlib import Path + + +def run(cmd: list[str], cwd: Path) -> None: + print(f"+ {' '.join(cmd)}", flush=True) + subprocess.run(cmd, cwd=str(cwd), check=True) + + +def stop_iotdb(dist_root: Path) -> None: + if sys.platform == "win32": + stop_script = dist_root / "sbin" / "windows" / "stop-standalone.bat" + else: + stop_script = dist_root / "sbin" / "stop-standalone.sh" + if not stop_script.is_file(): + print(f"stop script not found, skip stop: {stop_script}", file=sys.stderr) + return + print(f"Stopping IoTDB via {stop_script}") + subprocess.run([str(stop_script)], cwd=str(dist_root), check=False, shell=True) + time.sleep(15) + + +def start_iotdb(dist_root: Path, start_script: Path, wait_s: int) -> None: + if not start_script.is_file(): + raise FileNotFoundError(f"start script not found: {start_script}") + print(f"Starting IoTDB via {start_script}") + subprocess.Popen( + [str(start_script)], + cwd=str(dist_root), + shell=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f"Waiting {wait_s}s for IoTDB to become ready") + time.sleep(wait_s) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("build_dir", help="CMake build directory containing CTestTestfile.cmake") + parser.add_argument("dist_root", help="IoTDB distribution root") + parser.add_argument("fixtures_root", help="C++ test fixtures root") + parser.add_argument("scripts_root", help="Directory containing configure_iotdb_ssl_it.py") + parser.add_argument("start_script", help="Relative path to start-standalone script under dist sbin/") + parser.add_argument("--config", default="Release", help="CTest build configuration (MSVC)") + parser.add_argument("--wait-seconds", type=int, default=45, help="Seconds to wait after IoTDB start") + args = parser.parse_args() + + build_dir = Path(args.build_dir).resolve() + dist_root = Path(args.dist_root).resolve() + fixtures_root = Path(args.fixtures_root).resolve() + scripts_root = Path(args.scripts_root).resolve() + start_script = dist_root / "sbin" / args.start_script + + ctest_base = ["ctest", "-j", "1", "--output-on-failure"] + if args.config: + ctest_base.extend(["-C", args.config]) + + print("=== Phase 1: plain IoTDB (session IT + examples) ===") + run(ctest_base + ["-L", "plain"], build_dir) + + print("=== Phase 2: restart IoTDB with TLS (rpc SSL/NTLS IT) ===") + stop_iotdb(dist_root) + configure = scripts_root / "configure_iotdb_ssl_it.py" + run( + [sys.executable, str(configure), str(dist_root), str(fixtures_root), "enable"], + cwd=scripts_root, + ) + start_iotdb(dist_root, start_script, args.wait_seconds) + run(ctest_base + ["-L", "ssl"], build_dir) + print("=== Phase 2b: NTLS (no IoTDB; openssl s_server) ===") + run(ctest_base + ["-L", "ntls"], build_dir) + + print("All C++ integration test phases passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp new file mode 100644 index 000000000000..48a9fc5790cf --- /dev/null +++ b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp @@ -0,0 +1,159 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +std::string joinPath(const std::string& base, const std::string& name) { + if (base.empty()) { + return name; + } + const char sep = (base.find('\\') != std::string::npos) ? '\\' : '/'; + if (base.back() == '/' || base.back() == '\\') { + return base + name; + } + return base + sep + name; +} + +EVP_PKEY* readPrivateKeyPem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return key; +} + +X509* readCertificatePem(const std::string& path) { + BIO* bio = BIO_new_file(path.c_str(), "rb"); + if (bio == nullptr) { + return nullptr; + } + X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + return cert; +} + +void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { + unsigned char keyid[EVP_MAX_MD_SIZE]; + unsigned int keyidLen = 0; + if (X509_pubkey_digest(cert, EVP_sha1(), keyid, &keyidLen) == 1) { + PKCS12_add_localkeyid(bag, keyid, static_cast(keyidLen)); + } +} + +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, + const char* friendlyName, const std::string& password) { + PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); + PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); + addLocalKeyId(certbag, cert); + sk_PKCS12_SAFEBAG_push(bags, certbag); + + PKCS8_PRIV_KEY_INFO* p8 = EVP_PKEY2PKCS8(key); + if (p8 == nullptr) { + return; + } + PKCS12_SAFEBAG* keybag = PKCS12_SAFEBAG_create_pkcs8_encrypt( + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (keybag == nullptr) { + return; + } + PKCS12_add_friendlyname_utf8(keybag, friendlyName, -1); + addLocalKeyId(keybag, cert); + sk_PKCS12_SAFEBAG_push(bags, keybag); +} + +} // namespace + +int main(int argc, char** argv) { + if (argc < 2) { + std::cerr << "usage: gen_tlcp_dual_p12 \n"; + return 1; + } + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, nullptr); + const std::string dir = argv[1]; + const std::string password = "thrift"; + const std::string outPath = joinPath(dir, "tlcp-client-dual.p12"); + + X509* signCert = readCertificatePem(joinPath(dir, "client_sign.crt")); + EVP_PKEY* signKey = readPrivateKeyPem(joinPath(dir, "client_sign.key")); + X509* encCert = readCertificatePem(joinPath(dir, "client_enc.crt")); + EVP_PKEY* encKey = readPrivateKeyPem(joinPath(dir, "client_enc.key")); + if (signCert == nullptr || signKey == nullptr || encCert == nullptr || encKey == nullptr) { + std::cerr << "failed to read TLCP PEM fixtures\n"; + return 2; + } + + STACK_OF(PKCS12_SAFEBAG)* bags = sk_PKCS12_SAFEBAG_new_null(); + addCertAndKeyBags(bags, signCert, signKey, "client.sign", password); + addCertAndKeyBags(bags, encCert, encKey, "client.enc", password); + PKCS7* p7 = PKCS12_pack_p7encdata(NID_pbes2, password.c_str(), static_cast(password.size()), + nullptr, 0, 2048, bags); + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + if (p7 == nullptr) { + std::cerr << "failed to pack PKCS12 bags\n"; + return 3; + } + + PKCS12* p12 = PKCS12_init(NID_pkcs7_data); + STACK_OF(PKCS7)* safes = sk_PKCS7_new_null(); + sk_PKCS7_push(safes, p7); + if (PKCS12_pack_authsafes(p12, safes) != 1) { + sk_PKCS7_pop_free(safes, PKCS7_free); + PKCS12_free(p12); + std::cerr << "failed to pack PKCS12 authsafes\n"; + return 4; + } + sk_PKCS7_pop_free(safes, PKCS7_free); + + BIO* bio = BIO_new_file(outPath.c_str(), "wb"); + if (bio == nullptr || i2d_PKCS12_bio(bio, p12) != 1) { + std::cerr << "failed to write " << outPath << "\n"; + BIO_free(bio); + PKCS12_free(p12); + return 5; + } + BIO_free(bio); + PKCS12_free(p12); + X509_free(signCert); + EVP_PKEY_free(signKey); + X509_free(encCert); + EVP_PKEY_free(encKey); + std::cout << "wrote " << outPath << "\n"; + return 0; +} diff --git a/iotdb-client/client-cpp/third-party/README.md b/iotdb-client/client-cpp/third-party/README.md index 4cbdd1ed5692..d7b15db4c159 100644 --- a/iotdb-client/client-cpp/third-party/README.md +++ b/iotdb-client/client-cpp/third-party/README.md @@ -68,8 +68,8 @@ Alternatively copy files manually from the URLs listed in | Platform | Typical files | |------------|---------------| -| `linux/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (+ `openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is present) | -| `mac/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz` (Xcode CLT usually provides m4/flex/bison) | -| `windows/` | `thrift-0.23.0.tar.gz`, `boost_1_60_0.tar.gz`, `win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison already on `PATH`) | +| `linux/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4.0.tar.gz` (when `WITH_SSL=ON`, default) | +| `mac/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_84_0.tar.gz`, `tongsuo-8.4.0.tar.gz` (when `WITH_SSL=ON`, default; Xcode CLT usually provides m4/flex/bison) | +| `windows/` | `thrift-6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242.tar.gz`, `boost_1_60_0.tar.gz`, `tongsuo-8.4.0.tar.gz` (when `WITH_SSL=ON`, default), `win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison already on `PATH`) | Download URLs: see the *Offline build* table in [`README.md`](../README.md). diff --git a/pom.xml b/pom.xml index ac80880bd425..802aafff0268 100644 --- a/pom.xml +++ b/pom.xml @@ -830,6 +830,9 @@ **/db/qp/sql/gen/** **/ainode-example/** + + client-cpp/test/fixtures/** + test/fixtures/** From 413945f1550b7a41937ad0c5cb6b3a087227cf92 Mon Sep 17 00:00:00 2001 From: hongzhigao <761417898@qq.com> Date: Wed, 8 Jul 2026 00:05:55 +0800 Subject: [PATCH 14/24] Address C++ SSL review: provider option, OpenSSL-style API, Thrift verify test Add IOTDB_SSL_PROVIDER (TONGSUO default, SYSTEM for host OpenSSL TLS-only), caFile/certFile/keyFile builder aliases, dual PKCS#12 validation, and fix Thrift handshake tests to complete TLS before asserting trust-store behavior. --- iotdb-client/client-cpp/CMakeLists.txt | 32 ++++++++++--- iotdb-client/client-cpp/README.md | 43 +++++++++++++----- iotdb-client/client-cpp/README_zh.md | 28 ++++++++++-- .../client-cpp/cmake/FetchOpenSSL.cmake | 2 + .../client-cpp/cmake/FetchSystemOpenSSL.cmake | 35 +++++++++++++++ .../cmake/InstallOpenSSLRuntime.cmake | 5 +++ iotdb-client/client-cpp/pom.xml | 2 + .../src/include/AbstractSessionBuilder.h | 4 ++ .../client-cpp/src/include/SessionBuilder.h | 17 +++++++ .../client-cpp/src/include/SessionPool.h | 24 +++++++++- .../src/include/TableSessionBuilder.h | 17 +++++++ .../client-cpp/src/rpc/RpcSslUtils.cpp | 45 ++++++++++++++++++- iotdb-client/client-cpp/src/rpc/RpcSslUtils.h | 4 ++ .../client-cpp/src/session/Session.cpp | 2 + .../client-cpp/src/session/SessionPool.cpp | 12 +++++ .../test/cpp/RpcSslTlsMutualAuthTest.cpp | 6 ++- .../client-cpp/test/cpp/SslTestFixtures.cpp | 5 +++ 17 files changed, 257 insertions(+), 26 deletions(-) create mode 100644 iotdb-client/client-cpp/cmake/FetchSystemOpenSSL.cmake diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index 2581efc07d53..1d5176dd6eed 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -78,7 +78,12 @@ if(NOT MSVC) file(WRITE "${_iotdb_cxx11_abi_stamp}" "${_iotdb_cxx11_abi_stamp_value}") endif() -option(WITH_SSL "Build with Tongsuo SSL/TLS support" ON) +option(WITH_SSL "Build with SSL/TLS support" ON) + +# SSL provider: TONGSUO (default, bundled, TLCP-capable) or SYSTEM (host OpenSSL, TLS only). +# Release packages and CI always use TONGSUO. +set(IOTDB_SSL_PROVIDER "TONGSUO" CACHE STRING "SSL provider: TONGSUO or SYSTEM") +set_property(CACHE IOTDB_SSL_PROVIDER PROPERTY STRINGS TONGSUO SYSTEM) option(BUILD_TESTING "Build IT test executables" OFF) option(IOTDB_OFFLINE "Disable all network access during configure" OFF) set(IOTDB_SESSION_VERSION "0.0.0" @@ -123,7 +128,17 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(FetchBoost) # -> BOOST_INCLUDE_DIR (Thrift build only) include(FetchBuildTools) if(WITH_SSL) - include(FetchOpenSSL) + if(IOTDB_SSL_PROVIDER STREQUAL "SYSTEM") + include(FetchSystemOpenSSL) + add_compile_definitions(IOTDB_SSL_PROVIDER_SYSTEM) + message(STATUS "[OpenSSL] provider=SYSTEM (TLS only; TLCP/NTLS disabled)") + elseif(IOTDB_SSL_PROVIDER STREQUAL "TONGSUO") + include(FetchOpenSSL) + message(STATUS "[OpenSSL] provider=TONGSUO (bundled; TLS + TLCP/NTLS)") + else() + message(FATAL_ERROR + "Unknown IOTDB_SSL_PROVIDER='${IOTDB_SSL_PROVIDER}'. Use TONGSUO or SYSTEM.") + endif() include(InstallOpenSSLRuntime) endif() include(FetchThrift) @@ -149,11 +164,9 @@ if(UNIX AND NOT APPLE) SOVERSION "${IOTDB_SESSION_SOVERSION}") endif() -# When SSL is on we bundle the Tongsuo/OpenSSL-compatible shared libraries next to -# libiotdb_session in the package lib/ directory. Give the library an $ORIGIN-relative runtime -# search path so the loader finds them without LD_LIBRARY_PATH / install_name -# tweaks, keeping the SDK self-contained. -if(WITH_SSL) +# When SSL is on and Tongsuo is bundled, record an $ORIGIN-relative runtime path so the +# loader finds libssl/libcrypto next to libiotdb_session without LD_LIBRARY_PATH tweaks. +if(WITH_SSL AND IOTDB_SSL_BUNDLE_RUNTIME) if(APPLE) set_target_properties(iotdb_session PROPERTIES BUILD_RPATH "@loader_path" @@ -254,6 +267,7 @@ install(TARGETS iotdb_session # Ship the Tongsuo shared libraries we link against next to iotdb_session so the # packaged SDK is self-contained on machines without a system SSL library. +# Skipped when IOTDB_SSL_PROVIDER=SYSTEM (host OpenSSL is not redistributed). if(WITH_SSL) iotdb_install_openssl_runtime() endif() @@ -308,6 +322,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt" "cmake.generator=${CMAKE_GENERATOR}\n" "cmake.build.type=${CMAKE_BUILD_TYPE}\n" "with.ssl=${WITH_SSL}\n" + "iotdb.ssl.provider=${IOTDB_SSL_PROVIDER}\n" "iotdb.offline=${IOTDB_OFFLINE}\n" "iotdb.use.cxx11.abi=${IOTDB_USE_CXX11_ABI}\n" "iotdb.extra.cxx.flags=${IOTDB_EXTRA_CXX_FLAGS}\n") @@ -328,6 +343,9 @@ endif() message(STATUS "iotdb_session configuration summary:") message(STATUS " WITH_SSL = ${WITH_SSL}") +if(WITH_SSL) + message(STATUS " IOTDB_SSL_PROVIDER = ${IOTDB_SSL_PROVIDER}") +endif() message(STATUS " BUILD_TESTING = ${BUILD_TESTING}") message(STATUS " IOTDB_OFFLINE = ${IOTDB_OFFLINE}") message(STATUS " IOTDB_USE_CXX11_ABI = ${IOTDB_USE_CXX11_ABI}") diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index 30f85eae80cc..ee2da551abba 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -367,6 +367,7 @@ pass them as Maven properties (the POM maps them to `-D` options for CMake): | CMake variable | Maven property (`-D...`) | |----------------|--------------------------| | `WITH_SSL` | `with.ssl` (e.g. `-Dwith.ssl=ON`) | +| `IOTDB_SSL_PROVIDER` | `iotdb.ssl.provider` (`TONGSUO` or `SYSTEM`) | | `IOTDB_OFFLINE` | `iotdb.offline` | | `BUILD_TESTING` | `build.tests` | | `IOTDB_DEPS_DIR` | `iotdb.deps.dir` | @@ -378,7 +379,8 @@ etc. directly. | Option | Default | Purpose | |-----------------------|----------------------------------|----------------------------------------------------------------------------------------------------------| -| `WITH_SSL` | `ON` | Link against Tongsuo (OpenSSL-compatible) and bundle its runtime libraries. See *SSL* below. | +| `WITH_SSL` | `ON` | Enable SSL/TLS. See *SSL* below. | +| `IOTDB_SSL_PROVIDER` | `TONGSUO` | `TONGSUO`: bundled Tongsuo (TLS + TLCP, default for releases/CI). `SYSTEM`: host OpenSSL 3.x (TLS only). | | `BUILD_TESTING` | `OFF` (Maven sets `ON` for verify) | Build Catch2 IT executables (Catch2 v2.13.7 header downloaded at configure time). | | `CATCH2_INCLUDE_DIR` | (unset) | Pre-downloaded Catch2 include dir (Maven sets this under `target/test/catch2`). | | `IOTDB_OFFLINE` | `OFF` | Disallow any network access during configure. | @@ -512,15 +514,24 @@ the GNU autotools tarballs assume a POSIX shell environment. `iotdb_session` builds **with SSL/TLS by default** (`WITH_SSL=ON`). Disable it with `-Dwith.ssl=OFF` (Maven) or `-DWITH_SSL=OFF` (standalone CMake). -[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0** is -**always built from source** during configure (Apache-2.0 licensed, -OpenSSL-compatible API). It adds Chinese commercial cipher and TLCP protocol -support on top of standard TLS. The resulting `libssl` / `libcrypto` shared -libraries are **bundled into the package `lib/` directory** (next to -`iotdb_session`, which records an `$ORIGIN`/`@loader_path` runtime path) so the -published SDK is self-contained. +### SSL provider (`IOTDB_SSL_PROVIDER`) -Host prerequisites when `WITH_SSL=ON`: +| Value | Default | Behavior | +|-------|---------|----------| +| `TONGSUO` | yes | Build [Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0** from a pinned tag with verified tarball SHA256. Bundles `libssl`/`libcrypto` into the SDK `lib/` directory. Supports standard TLS and TLCP/NTLS. Used by CI and release packages. | +| `SYSTEM` | no | Link against the host OpenSSL 3.x installation (`find_package(OpenSSL)`). TLS only — TLCP/NTLS is disabled. Host SSL libraries are **not** bundled into the SDK zip. For source builds on machines that already ship OpenSSL. | + +Maven: `-Diotdb.ssl.provider=SYSTEM` (releases and CI stay on `TONGSUO`). +Standalone CMake: `-DIOTDB_SSL_PROVIDER=SYSTEM`. + +When `IOTDB_SSL_PROVIDER=TONGSUO`, Tongsuo is **always built from source** +during configure (Apache-2.0 licensed, OpenSSL-compatible API). It adds Chinese +commercial cipher and TLCP protocol support on top of standard TLS. The +resulting `libssl` / `libcrypto` shared libraries are **bundled into the package +`lib/` directory** (next to `iotdb_session`, which records an +`$ORIGIN`/`@loader_path` runtime path) so the published SDK is self-contained. + +Host prerequisites when `WITH_SSL=ON` and `IOTDB_SSL_PROVIDER=TONGSUO`: - **Linux / macOS** – `perl`, `make`, and a C compiler (Tongsuo `./config`). - **Windows** – Perl (e.g. Strawberry Perl) and `nmake` from the Visual Studio @@ -538,9 +549,19 @@ first. PEM CA files are supported via `trustStore` (PEM path) or the legacy | `trustStore` | Server trust material (PKCS#12 or PEM CA file) | Not JKS; `.p12`/`.pfx` = PKCS#12 | | `keyStore` | Client identity (PKCS#12 or PEM cert+key) | TLCP mutual auth needs dual-cert PKCS#12 | | `trustCertFilePath` | Legacy PEM CA path | Used only when `trustStore` is unset | +| `caFile` | OpenSSL-style alias for `trustCertFilePath` | PEM CA file for server trust | +| `certFile` / `keyFile` | OpenSSL-style PEM client identity | Both required; alternative to `keyStore` | + +**JKS is not supported** by the C++ client — convert to PKCS#12 first. + +Java-style `trustStore` / `keyStore` names are kept for parity with the Java +Session API. Native C++ users with PEM material can use `caFile()`, +`certFile()` / `keyFile()`, or the legacy `trustCertFilePath()` without +thinking in Java keystore terms. -OpenSSL-style PEM users can point `trustStore` at a `.pem` CA bundle, or use -`trustCertFilePath()` for the same PEM file without PKCS#12 wrapping. +OpenSSL-style PEM users can point `trustStore` at a `.pem` CA bundle, use +`caFile()` / `trustCertFilePath()` for the same PEM file, or wrap credentials +in PKCS#12 via `trustStore` / `keyStore`. **TLS one-way (server authentication):** diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index aeccfd6bc843..e8f5c63770b5 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -237,16 +237,28 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 | CMake 变量 | Maven 属性 | |------------|------------| | `WITH_SSL` | `with.ssl`(默认 `ON`,关闭用 `-Dwith.ssl=OFF`) | +| `IOTDB_SSL_PROVIDER` | `iotdb.ssl.provider`(`TONGSUO` 或 `SYSTEM`) | | `IOTDB_OFFLINE` | `iotdb.offline` | | `BUILD_TESTING` | `build.tests` | | `IOTDB_DEPS_DIR` | `iotdb.deps.dir` | | `BOOST_INCLUDEDIR` | `boost.include.dir` | | `CMAKE_BUILD_TYPE` | `cmake.build.type`,例如 `-Dcmake.build.type=Debug` | -SSL 默认开启(`WITH_SSL=ON`)。配置阶段**始终从源码构建** -[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0** -(OpenSSL 兼容 API,Apache-2.0,支持国密/TLCP),并把 `libssl`/`libcrypto` -动态库复制到产物 `lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 +SSL 默认开启(`WITH_SSL=ON`)。 + +**SSL 提供方(`IOTDB_SSL_PROVIDER`)** + +| 值 | 默认 | 说明 | +|----|------|------| +| `TONGSUO` | 是 | 从固定 tag 构建 [Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4.0**(校验 tarball SHA256),将 `libssl`/`libcrypto` 打入 SDK `lib/`。支持 TLS 与 TLCP/NTLS。CI 与发布包使用此选项。 | +| `SYSTEM` | 否 | 链接宿主机 OpenSSL 3.x(`find_package(OpenSSL)`),仅 TLS,不支持 TLCP/NTLS,不打包宿主机 SSL 库。适合已有 OpenSSL 的源码构建。 | + +Maven:`-Diotdb.ssl.provider=SYSTEM`(发布与 CI 仍为 `TONGSUO`)。 +独立 CMake:`-DIOTDB_SSL_PROVIDER=SYSTEM`。 + +`TONGSUO` 模式下,配置阶段**始终从源码构建** Tongsuo(OpenSSL 兼容 API, +Apache-2.0,支持国密/TLCP),并把 `libssl`/`libcrypto` 动态库复制到产物 +`lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。 ### 客户端 SSL / TLCP 配置 @@ -261,6 +273,14 @@ CA 可通过 `trustStore`(指向 `.pem` 文件)或遗留的 `trustCertFilePa | `trustStore` | 服务端信任材料(PKCS#12 或 PEM CA) | 非 JKS;`.p12`/`.pfx` 表示 PKCS#12 | | `keyStore` | 客户端身份(PKCS#12 或 PEM 证书+私钥) | TLCP 双向认证需双证书 PKCS#12 | | `trustCertFilePath` | 遗留 PEM CA 路径 | 仅 `trustStore` 未设置时使用 | +| `caFile` | OpenSSL 风格,等同 `trustCertFilePath` | PEM CA 文件 | +| `certFile` / `keyFile` | OpenSSL 风格 PEM 客户端证书与私钥 | 需同时设置;可替代 `keyStore` | + +**不支持 JKS**,需先转换为 PKCS#12。 + +为与 Java Session API 对齐保留 `trustStore` / `keyStore` 命名。持有 PEM +材料的 C++ 用户可直接使用 `caFile()`、`certFile()` / `keyFile()` 或遗留的 +`trustCertFilePath()`,无需按 Java keystore 概念配置。 **TLS 单向认证:** diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index e7aaa2ed6f16..a83a8804fe12 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -28,6 +28,8 @@ # can link against them unchanged. # ============================================================================= +set(IOTDB_SSL_BUNDLE_RUNTIME ON CACHE INTERNAL "Bundle Tongsuo SSL libs into SDK zip") + # --- Build Tongsuo ${TONGSUO_GIT_REF} from source --- if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]{7,40}$") set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") diff --git a/iotdb-client/client-cpp/cmake/FetchSystemOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchSystemOpenSSL.cmake new file mode 100644 index 000000000000..aa640c748098 --- /dev/null +++ b/iotdb-client/client-cpp/cmake/FetchSystemOpenSSL.cmake @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# ============================================================================= +# FetchSystemOpenSSL.cmake (IOTDB_SSL_PROVIDER=SYSTEM) +# +# Link against the host OpenSSL installation. TLS only — TLCP/NTLS is disabled +# at compile time. Release packages and CI use bundled Tongsuo instead. +# ============================================================================= + +find_package(OpenSSL REQUIRED) + +if(NOT OPENSSL_VERSION_MAJOR VERSION_GREATER_EQUAL 3) + message(WARNING + "[OpenSSL] system provider: OpenSSL ${OPENSSL_VERSION} found; " + "OpenSSL 3.x is recommended") +endif() + +set(IOTDB_SSL_BUNDLE_RUNTIME OFF CACHE INTERNAL "Do not bundle system SSL into SDK zip") + +message(STATUS "[OpenSSL] using system SSL provider at ${OPENSSL_ROOT_DIR}") diff --git a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake index f3e181b8e8ff..5f828a2f9f85 100644 --- a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake +++ b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake @@ -77,6 +77,11 @@ function(_iotdb_collect_openssl_windows_dlls _out_var) endfunction() function(iotdb_install_openssl_runtime) + if(DEFINED IOTDB_SSL_BUNDLE_RUNTIME AND NOT IOTDB_SSL_BUNDLE_RUNTIME) + message(STATUS "[OpenSSL] system provider: skip bundling host SSL into SDK lib/") + return() + endif() + if(WIN32) _iotdb_collect_openssl_windows_dlls(_dlls) if(NOT _dlls) diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index f0e9fcd116f6..80fe04541963 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -50,6 +50,7 @@ ${project.basedir}/third-party OFF ON + TONGSUO 6dfb0b26ea6b9ab9c114e0ef4c0f6e7b8110b242 8.4.0 57c2741750a699bfbdaa1bbe44a5733e9c8fc65d086c210151cfbc2bbd6fc975 @@ -116,6 +117,7 @@ + diff --git a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h index 6217a2e73761..3da883ea04d9 100644 --- a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h @@ -62,6 +62,10 @@ class AbstractSessionBuilder { std::string trustStorePwd; std::string keyStore; std::string keyStorePwd; + /** OpenSSL-style PEM client certificate path (paired with keyFilePath). */ + std::string certFilePath; + /** OpenSSL-style PEM client private key path (paired with certFilePath). */ + std::string keyFilePath; }; #endif // IOTDB_ABSTRACTSESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/SessionBuilder.h b/iotdb-client/client-cpp/src/include/SessionBuilder.h index 5d3eabd7434e..a1b64b2e9952 100644 --- a/iotdb-client/client-cpp/src/include/SessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/SessionBuilder.h @@ -44,6 +44,11 @@ class SessionBuilder : public AbstractSessionBuilder { return this; } + /** OpenSSL-style alias for trustCertFilePath (PEM CA file for server trust). */ + SessionBuilder* caFile(const std::string& path) { + return trustCertFilePath(path); + } + SessionBuilder* sslProtocol(const std::string& sslProtocol) { AbstractSessionBuilder::sslProtocol = sslProtocol; return this; @@ -69,6 +74,18 @@ class SessionBuilder : public AbstractSessionBuilder { return this; } + /** OpenSSL-style PEM client certificate path (requires keyFile). */ + SessionBuilder* certFile(const std::string& path) { + AbstractSessionBuilder::certFilePath = path; + return this; + } + + /** OpenSSL-style PEM client private key path (requires certFile). */ + SessionBuilder* keyFile(const std::string& path) { + AbstractSessionBuilder::keyFilePath = path; + return this; + } + SessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/include/SessionPool.h b/iotdb-client/client-cpp/src/include/SessionPool.h index c71580262446..cce4528ea5bf 100644 --- a/iotdb-client/client-cpp/src/include/SessionPool.h +++ b/iotdb-client/client-cpp/src/include/SessionPool.h @@ -193,8 +193,10 @@ class SessionPool { SessionPool& setTrustStorePwd(std::string trustStorePwd); SessionPool& setKeyStore(std::string keyStore); SessionPool& setKeyStorePwd(std::string keyStorePwd); + SessionPool& setCertFilePath(std::string path); + SessionPool& setKeyFilePath(std::string path); - // Borrow a Session. Blocks until one is free or a new one can be created, + // Borrow a Session. // up to timeoutMs (<= 0 means use the pool default). Throws IoTDBException on // timeout or when the pool is closed. PooledSession getSession(); @@ -259,6 +261,8 @@ class SessionPool { std::string trustStorePwd_; std::string keyStore_; std::string keyStorePwd_; + std::string certFilePath_; + std::string keyFilePath_; // pool sizing / waiting policy size_t maxSize_; @@ -349,6 +353,10 @@ class SessionPoolBuilder : public AbstractSessionBuilder { AbstractSessionBuilder::trustCertFilePath = v; return this; } + /** OpenSSL-style alias for trustCertFilePath (PEM CA file for server trust). */ + SessionPoolBuilder* caFile(const std::string& v) { + return trustCertFilePath(v); + } SessionPoolBuilder* sslProtocol(const std::string& v) { AbstractSessionBuilder::sslProtocol = v; return this; @@ -369,6 +377,16 @@ class SessionPoolBuilder : public AbstractSessionBuilder { AbstractSessionBuilder::keyStorePwd = v; return this; } + /** OpenSSL-style PEM client certificate path (requires keyFile). */ + SessionPoolBuilder* certFile(const std::string& v) { + AbstractSessionBuilder::certFilePath = v; + return this; + } + /** OpenSSL-style PEM client private key path (requires certFile). */ + SessionPoolBuilder* keyFile(const std::string& v) { + AbstractSessionBuilder::keyFilePath = v; + return this; + } SessionPoolBuilder* maxSize(size_t v) { maxSize_ = v; return this; @@ -415,7 +433,9 @@ class SessionPoolBuilder : public AbstractSessionBuilder { .setTrustStore(AbstractSessionBuilder::trustStore) .setTrustStorePwd(AbstractSessionBuilder::trustStorePwd) .setKeyStore(AbstractSessionBuilder::keyStore) - .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd); + .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd) + .setCertFilePath(AbstractSessionBuilder::certFilePath) + .setKeyFilePath(AbstractSessionBuilder::keyFilePath); return pool; } diff --git a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h index 0642acf0759b..2e8a8ff72d02 100644 --- a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h @@ -55,6 +55,11 @@ class TableSessionBuilder : public AbstractSessionBuilder { return this; } + /** OpenSSL-style alias for trustCertFilePath (PEM CA file for server trust). */ + TableSessionBuilder* caFile(const std::string& path) { + return trustCertFilePath(path); + } + TableSessionBuilder* sslProtocol(const std::string& sslProtocol) { AbstractSessionBuilder::sslProtocol = sslProtocol; return this; @@ -80,6 +85,18 @@ class TableSessionBuilder : public AbstractSessionBuilder { return this; } + /** OpenSSL-style PEM client certificate path (requires keyFile). */ + TableSessionBuilder* certFile(const std::string& path) { + AbstractSessionBuilder::certFilePath = path; + return this; + } + + /** OpenSSL-style PEM client private key path (requires certFile). */ + TableSessionBuilder* keyFile(const std::string& path) { + AbstractSessionBuilder::keyFilePath = path; + return this; + } + TableSessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp index 0158a49129d8..c14d5db599fc 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -336,6 +336,21 @@ void loadTlsIdentityFromPem(SSL_CTX* ctx, const std::string& path) { } } +void loadTlsIdentityFromPemFiles(SSL_CTX* ctx, const std::string& certPath, + const std::string& keyPath) { + ensureFileReadable(certPath, "Client certificate"); + ensureFileReadable(keyPath, "Client private key"); + if (SSL_CTX_use_certificate_file(ctx, certPath.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client certificate from " + certPath); + } + if (SSL_CTX_use_PrivateKey_file(ctx, keyPath.c_str(), SSL_FILETYPE_PEM) != 1) { + throwSslError("Failed to load PEM client private key from " + keyPath); + } + if (SSL_CTX_check_private_key(ctx) != 1) { + throwSslError("Client certificate and private key do not match"); + } +} + void loadTlsKeyStore(SSL_CTX* ctx, const std::string& path, const std::string& password) { rejectJksPath(path, "Key store"); ensureFileReadable(path, "Key store"); @@ -519,6 +534,13 @@ SSL_CTX* createTlsClientContext(const SslConfig& config) { } if (hasText(config.keyStore)) { loadTlsKeyStore(ctx, config.keyStore, config.keyStorePwd); + } else if (hasText(config.certFilePath) || hasText(config.keyFilePath)) { + if (!hasText(config.certFilePath) || !hasText(config.keyFilePath)) { + SSL_CTX_free(ctx); + throw IoTDBException( + "certFile and keyFile must both be set for PEM client identity"); + } + loadTlsIdentityFromPemFiles(ctx, config.certFilePath, config.keyFilePath); } return ctx; } @@ -552,8 +574,22 @@ void validatePkcs12Store(const std::string& path, const std::string& password) { Pkcs12ParsedIdentity parsed; if (PKCS12_parse(p12, password.empty() ? nullptr : password.c_str(), &parsed.pkey, &parsed.cert, &parsed.ca) != 1) { + bool foundCert = false; + forEachPkcs12Bag(p12, password, [&](PKCS12_SAFEBAG* bag) { + if (PKCS12_SAFEBAG_get_nid(bag) == NID_certBag) { + X509* bagCert = PKCS12_certbag2x509(bag); + if (bagCert != nullptr) { + validateCertificate(bagCert); + X509_free(bagCert); + foundCert = true; + } + } + }); PKCS12_free(p12); - throw IoTDBException("Failed to parse PKCS12 store: " + path); + if (!foundCert) { + throw IoTDBException("Failed to parse PKCS12 store: " + path); + } + return; } if (parsed.cert != nullptr) { validateCertificate(parsed.cert); @@ -674,6 +710,13 @@ void RpcSslUtils::enableNtlsOnSsl(SSL* ssl) { SSL_CTX* RpcSslUtils::createClientSslContext(const SslConfig& config) { const std::string protocol = resolveProtocol(config.sslProtocol); +#ifdef IOTDB_SSL_PROVIDER_SYSTEM + if (isTlcpProtocol(protocol)) { + throw IoTDBException( + "TLCP/NTLS requires IOTDB_SSL_PROVIDER=TONGSUO; " + "rebuild with the default bundled Tongsuo provider"); + } +#endif if (isTlcpProtocol(protocol)) { return createTlcpClientContext(config); } diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h index 1b717f714d3a..f8ee4b1a5bf8 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h @@ -44,6 +44,10 @@ struct SslConfig { std::string keyStorePwd; /** Legacy PEM trust certificate path; used when trustStore is empty. */ std::string trustCertFilePath; + /** OpenSSL-style PEM client certificate path (used with keyFilePath). */ + std::string certFilePath; + /** OpenSSL-style PEM client private key path (used with certFilePath). */ + std::string keyFilePath; std::string effectiveTrustStore() const; }; diff --git a/iotdb-client/client-cpp/src/session/Session.cpp b/iotdb-client/client-cpp/src/session/Session.cpp index 4b24b992461e..67abbe447a44 100644 --- a/iotdb-client/client-cpp/src/session/Session.cpp +++ b/iotdb-client/client-cpp/src/session/Session.cpp @@ -549,6 +549,8 @@ Session::Session(AbstractSessionBuilder* builder) : impl_(new Impl()) { impl_->sslConfig_.keyStore = builder->keyStore; impl_->sslConfig_.keyStorePwd = builder->keyStorePwd; impl_->sslConfig_.trustCertFilePath = builder->trustCertFilePath; + impl_->sslConfig_.certFilePath = builder->certFilePath; + impl_->sslConfig_.keyFilePath = builder->keyFilePath; impl_->initZoneId(); impl_->initNodesSupplier(impl_->nodeUrls_); } diff --git a/iotdb-client/client-cpp/src/session/SessionPool.cpp b/iotdb-client/client-cpp/src/session/SessionPool.cpp index 42961dbaff61..6c932f73eef0 100644 --- a/iotdb-client/client-cpp/src/session/SessionPool.cpp +++ b/iotdb-client/client-cpp/src/session/SessionPool.cpp @@ -134,6 +134,16 @@ SessionPool& SessionPool::setKeyStorePwd(std::string keyStorePwd) { return *this; } +SessionPool& SessionPool::setCertFilePath(std::string path) { + certFilePath_ = std::move(path); + return *this; +} + +SessionPool& SessionPool::setKeyFilePath(std::string path) { + keyFilePath_ = std::move(path); + return *this; +} + std::shared_ptr SessionPool::constructNewSession() { AbstractSessionBuilder builder; builder.host = host_; @@ -156,6 +166,8 @@ std::shared_ptr SessionPool::constructNewSession() { builder.trustStorePwd = trustStorePwd_; builder.keyStore = keyStore_; builder.keyStorePwd = keyStorePwd_; + builder.certFilePath = certFilePath_; + builder.keyFilePath = keyFilePath_; auto session = std::make_shared(&builder); session->open(enableRPCCompression_, connectTimeoutMs_); diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp index fcf96f51dddc..012b1db2c797 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp @@ -152,8 +152,12 @@ TEST_CASE("Thrift TSSLSocketFactory verifies server certificate when trust store REQUIRE(ssltest::thriftTlsHandshakeWithSslConfig(goodConfig, "127.0.0.1", server.port())); SslConfig badConfig = goodConfig; - badConfig.trustStore = ssltest::tlsFixture("tls-client.p12"); + // Use an unrelated CA so server cert verification must fail (tls-client.p12 can + // still include the issuing CA in its PKCS#12 chain and would not be a negative case). + badConfig.trustStore = ssltest::tlcpFixture("ca.crt"); + badConfig.trustStorePwd.clear(); badConfig.keyStore.clear(); + REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(badConfig, "127.0.0.1", server.port())); REQUIRE_FALSE(ssltest::thriftTlsHandshakeWithSslConfig(badConfig, "127.0.0.1", server.port())); server.stop(); #endif diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 9c4a02bc8fa1..0b5be7b9de7a 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -53,6 +53,7 @@ #endif #include "RpcSslUtils.h" +#include "Common.h" #include "SslTestFixtures.h" #include @@ -486,6 +487,10 @@ bool thriftTlsHandshakeWithSslConfig(const SslConfig& config, const std::string& std::shared_ptr socket = factory->createSocket(host, port); socket->open(); + // TSSLSocket::open() only completes the TCP connect; TLS handshake (and + // certificate verification) runs on the first read/write. + static const char kHttpProbe[] = "GET /\r\n"; + socket->write(reinterpret_cast(kHttpProbe), sizeof(kHttpProbe) - 1); socket->close(); return true; } catch (const apache::thrift::transport::TTransportException&) { From 14bbad836ac57b5ea5338feac8022d7e4b43802c Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Mon, 7 Sep 2026 14:31:11 +0800 Subject: [PATCH 15/24] feat(client-cpp): support configurable NTLS providers --- LICENSE-binary | 2 + iotdb-client/client-cpp/CMakeLists.txt | 21 +- iotdb-client/client-cpp/README.md | 51 ++- iotdb-client/client-cpp/README_zh.md | 41 ++- .../client-cpp/cmake/FetchOpenSSL.cmake | 142 +++++++- .../client-cpp/cmake/FetchThrift.cmake | 20 +- .../cmake/InstallOpenSSLRuntime.cmake | 13 +- iotdb-client/client-cpp/pom.xml | 4 + .../third_party/DEPENDENCIES.md | 3 +- .../package-metadata/third_party/NOTICE | 5 + .../src/include/AbstractSessionBuilder.h | 3 + .../client-cpp/src/include/SessionBuilder.h | 15 + .../client-cpp/src/include/SessionC.h | 4 + .../client-cpp/src/include/SessionPool.h | 23 +- .../src/include/TableSessionBuilder.h | 15 + .../client-cpp/src/rpc/GmsslTlcpSocket.cpp | 197 ++++++++++ .../client-cpp/src/rpc/GmsslTlcpSocket.h | 60 ++++ .../client-cpp/src/rpc/RpcSslUtils.cpp | 198 +++++++++- iotdb-client/client-cpp/src/rpc/RpcSslUtils.h | 14 + .../client-cpp/src/rpc/SessionConnection.cpp | 7 + .../client-cpp/src/rpc/SessionConnection.h | 4 +- .../client-cpp/src/rpc/ThriftConnection.cpp | 7 + .../client-cpp/src/rpc/ThriftConnection.h | 4 +- .../client-cpp/src/session/Session.cpp | 3 + .../client-cpp/src/session/SessionC.cpp | 45 +++ .../client-cpp/src/session/SessionPool.cpp | 18 + iotdb-client/client-cpp/test/CMakeLists.txt | 62 +++- .../client-cpp/test/cpp/RpcGmsslNtlsTest.cpp | 338 ++++++++++++++++++ .../client-cpp/test/cpp/RpcNtlsE2eTest.cpp | 65 +++- .../test/cpp/RpcSslTlcpMutualAuthTest.cpp | 50 +++ .../client-cpp/test/cpp/RpcSslUtilsTest.cpp | 3 +- .../client-cpp/test/fixtures/gmssl/ca.crt | 11 + .../client-cpp/test/fixtures/gmssl/client.crt | 11 + .../client-cpp/test/fixtures/gmssl/client.key | 8 + .../test/fixtures/gmssl/server-certs.pem | 35 ++ .../test/fixtures/gmssl/server-keys.pem | 16 + .../test/fixtures/tlcp/intermediate_ca.crt | 12 + .../fixtures/tlcp/intermediate_client_enc.crt | 12 + .../fixtures/tlcp/intermediate_client_enc.key | 8 + .../tlcp/intermediate_client_sign.crt | 12 + .../tlcp/intermediate_client_sign.key | 8 + .../test/fixtures/tlcp/intermediate_root.crt | 12 + 42 files changed, 1501 insertions(+), 81 deletions(-) create mode 100644 iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.cpp create mode 100644 iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.h create mode 100644 iotdb-client/client-cpp/test/cpp/RpcGmsslNtlsTest.cpp create mode 100644 iotdb-client/client-cpp/test/fixtures/gmssl/ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/gmssl/client.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/gmssl/client.key create mode 100644 iotdb-client/client-cpp/test/fixtures/gmssl/server-certs.pem create mode 100644 iotdb-client/client-cpp/test/fixtures/gmssl/server-keys.pem create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_ca.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.crt create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.key create mode 100644 iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_root.crt diff --git a/LICENSE-binary b/LICENSE-binary index 58c794bd8024..a92f3b13fe23 100644 --- a/LICENSE-binary +++ b/LICENSE-binary @@ -246,6 +246,7 @@ io.dropwizard.metrics:metrics-core:4.2.19 io.dropwizard.metrics:metrics-jvm:3.2.2 com.librato.metrics:metrics-librato:5.1.0 com.github.moquette-io.moquette:moquette-broker:0.18 +GmSSL:GmSSL:3.2.x (optional C++ NTLS provider) io.netty:netty-buffer:4.1.137.Final io.netty:netty-codec:4.1.137.Final io.netty:netty-codec-http:4.1.137.Final @@ -261,6 +262,7 @@ org.osgi:org.osgi.core:7.0.0 org.osgi:osgi.cmpn:7.0.0 org.ops4j.pax.jdbc:pax-jdbc-common:1.5.6 org.xerial.snappy:snappy-java:1.1.10.5 +Tongsuo:Tongsuo:8.4-stable (default C++ NTLS provider) io.airlift.airline:0.9 diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index 0884381a2bea..9364d24aaad5 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -78,7 +78,7 @@ if(NOT MSVC) file(WRITE "${_iotdb_cxx11_abi_stamp}" "${_iotdb_cxx11_abi_stamp_value}") endif() -option(WITH_SSL "Build with Tongsuo SSL/TLS support" ON) +option(WITH_SSL "Build with SSL/TLS support" ON) option(BUILD_TESTING "Build IT test executables" OFF) option(IOTDB_OFFLINE "Disable all network access during configure" OFF) set(IOTDB_SESSION_VERSION "0.0.0" @@ -99,8 +99,17 @@ set(BOOST_VERSION "${_iotdb_default_boost_version}" CACHE STRING "Boost version used when downloading / unpacking (Thrift build only)") set(THRIFT_VERSION "0.24.0" CACHE STRING "Apache Thrift version used when downloading / building") +set(IOTDB_NTLS_PROVIDER "TONGSUO" + CACHE STRING "NTLS provider: TONGSUO (default) or GMSSL") +set_property(CACHE IOTDB_NTLS_PROVIDER PROPERTY STRINGS TONGSUO GMSSL) +string(TOUPPER "${IOTDB_NTLS_PROVIDER}" IOTDB_NTLS_PROVIDER) +if(NOT IOTDB_NTLS_PROVIDER MATCHES "^(TONGSUO|GMSSL)$") + message(FATAL_ERROR "IOTDB_NTLS_PROVIDER must be TONGSUO or GMSSL") +endif() set(TONGSUO_GIT_REF "8.4-stable" CACHE STRING "Tongsuo git ref used when building SSL/TLS from source") +set(IOTDB_GMSSL_ROOT_DIR "" + CACHE PATH "Preinstalled GmSSL 3.2 root (required for the GMSSL provider)") if(WIN32) set(IOTDB_OS_DEPS_DIR "${IOTDB_DEPS_DIR}/windows") @@ -196,8 +205,14 @@ else() endif() if(WITH_SSL) - target_link_libraries(iotdb_session PUBLIC OpenSSL::SSL OpenSSL::Crypto) target_compile_definitions(iotdb_session PUBLIC WITH_SSL=1) + if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + target_link_libraries(iotdb_session PRIVATE IoTDB::gmssl) + target_compile_definitions(iotdb_session PUBLIC IOTDB_NTLS_PROVIDER_GMSSL=1) + else() + target_link_libraries(iotdb_session PUBLIC OpenSSL::SSL OpenSSL::Crypto) + target_compile_definitions(iotdb_session PUBLIC IOTDB_NTLS_PROVIDER_TONGSUO=1) + endif() else() target_compile_definitions(iotdb_session PUBLIC WITH_SSL=0) endif() @@ -298,6 +313,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt" "cmake.generator=${CMAKE_GENERATOR}\n" "cmake.build.type=${CMAKE_BUILD_TYPE}\n" "with.ssl=${WITH_SSL}\n" + "ntls.provider=${IOTDB_NTLS_PROVIDER}\n" "iotdb.offline=${IOTDB_OFFLINE}\n" "iotdb.use.cxx11.abi=${IOTDB_USE_CXX11_ABI}\n" "iotdb.extra.cxx.flags=${IOTDB_EXTRA_CXX_FLAGS}\n") @@ -318,6 +334,7 @@ endif() message(STATUS "iotdb_session configuration summary:") message(STATUS " WITH_SSL = ${WITH_SSL}") +message(STATUS " IOTDB_NTLS_PROVIDER = ${IOTDB_NTLS_PROVIDER}") message(STATUS " BUILD_TESTING = ${BUILD_TESTING}") message(STATUS " IOTDB_OFFLINE = ${IOTDB_OFFLINE}") message(STATUS " IOTDB_USE_CXX11_ABI = ${IOTDB_USE_CXX11_ABI}") diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index a66068d6e28d..ed6d1b057486 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -367,6 +367,8 @@ pass them as Maven properties (the POM maps them to `-D` options for CMake): | CMake variable | Maven property (`-D...`) | |----------------|--------------------------| | `WITH_SSL` | `with.ssl` (e.g. `-Dwith.ssl=ON`) | +| `IOTDB_NTLS_PROVIDER` | `ntls.provider` (`TONGSUO` or `GMSSL`) | +| `IOTDB_GMSSL_ROOT_DIR` | `gmssl.root.dir` | | `IOTDB_OFFLINE` | `iotdb.offline` | | `BUILD_TESTING` | `build.tests` | | `IOTDB_DEPS_DIR` | `iotdb.deps.dir` | @@ -385,7 +387,9 @@ etc. directly. | `IOTDB_DEPS_DIR` | `/third-party` | Override the local tarball cache directory. | | `BOOST_VERSION` | `1.60.0` (`1.84.0` on macOS) | Boost version that CMake will look for / download. | | `THRIFT_VERSION` | `0.24.0` | Apache Thrift version to build from source. | +| `IOTDB_NTLS_PROVIDER` | `TONGSUO` | NTLS provider: `TONGSUO` or `GMSSL`. | | `TONGSUO_GIT_REF` | `8.4-stable` | Tongsuo git ref built from source when `WITH_SSL=ON`. | +| `IOTDB_GMSSL_ROOT_DIR` | (unset) | Preinstalled GmSSL 3 root required by the `GMSSL` provider. | | `BOOST_ROOT` | (unset) | Existing Boost install to reuse, equivalent to `-Dboost.include.dir=...` from the legacy build. | | `CMAKE_INSTALL_PREFIX`| `/install` | Install location. | | `CMAKE_BUILD_TYPE` | `Release` | Single-config generator build type. Use `Debug` to produce a debug library. | @@ -508,18 +512,23 @@ the GNU autotools tarballs assume a POSIX shell environment. ## SSL -`iotdb_session` builds **with SSL/TLS by default** (`WITH_SSL=ON`). Disable -it with `-Dwith.ssl=OFF` (Maven) or `-DWITH_SSL=OFF` (standalone CMake). +`iotdb_session` builds with SSL/TLS by default. Supported NTLS providers: -[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4-stable** is -**always built from source** during configure (Apache-2.0 licensed, -OpenSSL-compatible API). It adds Chinese commercial cipher and TLCP protocol -support on top of standard TLS. The resulting `libssl` / `libcrypto` shared -libraries are **bundled into the package `lib/` directory** (next to -`iotdb_session`, which records an `$ORIGIN`/`@loader_path` runtime path) so the -published SDK is self-contained. +- `TONGSUO` (default): Tongsuo 8.4-stable, built from source; TLS/TLCP and + PKCS12 or PEM credentials. +- `GMSSL`: preinstalled GmSSL 3.2 using its native TLCP API; TLCP with PEM + credentials. OCL is not used because it does not implement + the complete OpenSSL API required by Thrift. -Host prerequisites when `WITH_SSL=ON`: +Select GmSSL with +`-DIOTDB_NTLS_PROVIDER=GMSSL -DIOTDB_GMSSL_ROOT_DIR=`. Provider runtime +libraries are bundled into the package `lib/` directory. GmSSL supports TLCP +only: set `sslProtocol("TLCP")`. PKCS12 `keyStore` is rejected; mutual +authentication requires the TLCP PEM certificate and private-key setters. +CMake probes ABI-affecting `ENABLE_*` symbols and validates the GmSSL 3.2 +headers/library pair during configuration. + +Host prerequisites for the default `TONGSUO` provider: - **Linux / macOS** – `perl`, `make`, and a C compiler (Tongsuo `./config`). - **Windows** – Perl (e.g. Strawberry Perl) and `nmake` from the Visual Studio @@ -527,8 +536,8 @@ Host prerequisites when `WITH_SSL=ON`: ### Client SSL / TLCP configuration -The C++ client mirrors the Java Session API. Use **PKCS12** (`.p12` / `.pfx`) -for `trustStore` and `keyStore`. JKS files must be converted to PKCS12 first +The C++ client mirrors the Java Session API. `trustStore` accepts PKCS12 or PEM +with Tongsuo and PEM with GmSSL. JKS files must be converted first (the C++ client does not parse JKS). **TLS one-way (server authentication):** @@ -591,6 +600,24 @@ auto session = SessionBuilder() ->build(); ``` +For Tongsuo PEM, put signing/encryption certificates (then the CA chain) in one +file and both private keys in another. For GmSSL, use the client signing +certificate followed by its intermediate chain, plus the matching single +private key: + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.pem") + ->tlcpCertChainFile("/path/to/client-certs.pem") + ->tlcpPrivateKeyFile("/path/to/client-keys.pem") + ->tlcpPrivateKeyPwd("secret") + ->build(); +``` + The legacy `trustCertFilePath()` setter still works as an alias for a PEM CA file when `trustStore` is not set. diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index 45440c3c7996..fd35578c6435 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -237,23 +237,34 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 | CMake 变量 | Maven 属性 | |------------|------------| | `WITH_SSL` | `with.ssl`(默认 `ON`,关闭用 `-Dwith.ssl=OFF`) | +| `IOTDB_NTLS_PROVIDER` | `ntls.provider`(`TONGSUO` 或 `GMSSL`) | +| `IOTDB_GMSSL_ROOT_DIR` | `gmssl.root.dir` | | `IOTDB_OFFLINE` | `iotdb.offline` | | `BUILD_TESTING` | `build.tests` | | `IOTDB_DEPS_DIR` | `iotdb.deps.dir` | | `BOOST_INCLUDEDIR` | `boost.include.dir` | | `CMAKE_BUILD_TYPE` | `cmake.build.type`,例如 `-Dcmake.build.type=Debug` | -SSL 默认开启(`WITH_SSL=ON`)。Apache Thrift 0.24.0 和 -[Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) **8.4-stable** -均在配置阶段从源码构建。Tongsuo 提供 OpenSSL 兼容 API(Apache-2.0,支持国密/TLCP), -构建会把 `libssl`/`libcrypto` -动态库复制到产物 `lib/` 目录。Windows 需要 Perl 与 VS 的 `nmake`。 +SSL 默认开启(`WITH_SSL=ON`)。支持的 NTLS Provider: + +- `TONGSUO`(默认):源码构建 Tongsuo 8.4-stable,支持 TLS/TLCP 及 + PKCS12、PEM 凭据。 +- `GMSSL`:使用预安装的 GmSSL 3.2 原生 TLCP API,支持 TLCP 及 PEM 凭据。 + OCL 未实现 Thrift 所需的完整 OpenSSL API,因此不使用 OCL。 + +选择 GmSSL 时传入 +`-DIOTDB_NTLS_PROVIDER=GMSSL -DIOTDB_GMSSL_ROOT_DIR=`。 +Provider 动态库会复制到产物 `lib/` 目录。GmSSL 仅支持 TLCP,必须设置 +`sslProtocol("TLCP")`;PKCS12 `keyStore` 会被拒绝,双向认证需使用 TLCP PEM +证书和私钥 setter。配置阶段会根据库符号自动推导影响 ABI 的 `ENABLE_*` +定义,并校验 GmSSL 3.2 的头文件/动态库 ABI。Tongsuo 在 Windows 上需要 +Perl 与 VS 的 `nmake`。 直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。 ### 客户端 SSL / TLCP 配置 -C++ 客户端 API 与 Java Session 对齐。`trustStore` 与 `keyStore` 请使用 -**PKCS12**(`.p12` / `.pfx`)。JKS 需先转换为 PKCS12(C++ 端不解析 JKS)。 +C++ 客户端 API 与 Java Session 对齐。Tongsuo 的 `trustStore` 支持 PKCS12 或 PEM, +GmSSL 使用 PEM;C++ 端不解析 JKS。 **TLS 单向认证:** @@ -296,6 +307,22 @@ auto session = SessionBuilder() ->build(); ``` +Tongsuo 使用 PEM 时,将签名/加密证书及 CA 链合并到一个文件,并将两个私钥合并到另一个文件。 +GmSSL 则使用客户端签名证书及其中间证书链,以及对应的单个私钥: + +```cpp +auto session = SessionBuilder() + .host("127.0.0.1") + ->rpcPort(6667) + ->useSSL(true) + ->sslProtocol("TLCP") + ->trustStore("/path/to/ca.pem") + ->tlcpCertChainFile("/path/to/client-certs.pem") + ->tlcpPrivateKeyFile("/path/to/client-keys.pem") + ->tlcpPrivateKeyPwd("secret") + ->build(); +``` + 旧版 `trustCertFilePath()` 在未设置 `trustStore` 时仍可作为 PEM CA 路径使用。 **C API**(在 `ts_session_open` / `ts_table_session_open` 之前配置): diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index 091096afbc95..17be39c9ae31 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -18,17 +18,16 @@ # ============================================================================= # FetchOpenSSL.cmake (only included when WITH_SSL=ON) # -# Builds Tongsuo (OpenSSL-compatible, Apache-2.0) from source for Thrift -# TSSLSocket and iotdb_session. Tongsuo adds Chinese commercial cipher / TLCP -# support on top of the standard TLS stack. +# Resolves the selected NTLS provider. Tongsuo is built from source for +# Thrift TSSLSocket; GmSSL uses a preinstalled native TLCP library. # # Side effects: -# Sets OPENSSL_ROOT_DIR to the local Tongsuo install tree, then defines -# imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package so callers -# can link against them unchanged. +# TONGSUO defines OpenSSL::SSL / OpenSSL::Crypto; GMSSL defines IoTDB::gmssl. +# IOTDB_NTLS_RUNTIME_LIBRARIES lists the selected provider's runtime files. # ============================================================================= -# --- Build Tongsuo ${TONGSUO_GIT_REF} from source --- +# --- Default provider: build Tongsuo ${TONGSUO_GIT_REF} from source --- +if(IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]{7,40}$") set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") set(_tongsuo_url "https://github.com/Tongsuo-Project/Tongsuo/archive/${TONGSUO_GIT_REF}.tar.gz") @@ -153,7 +152,8 @@ if(NOT EXISTS "${_tongsuo_stamp}") set(_tongsuo_target "VC-WIN64A") message(STATUS "[Tongsuo] configuring (${_tongsuo_target}) -> ${_tongsuo_inst}") execute_process( - COMMAND "${PERL_EXECUTABLE}" Configure enable-ntls no-asm ${_tongsuo_target} + COMMAND "${CMAKE_COMMAND}" -E env "CC=cl" "CXX=cl" + "${PERL_EXECUTABLE}" Configure enable-ntls no-asm ${_tongsuo_target} --prefix=${_tongsuo_inst} --openssldir=${_tongsuo_inst}/ssl WORKING_DIRECTORY "${_tongsuo_src}" @@ -214,4 +214,130 @@ unset(OPENSSL_INCLUDE_DIR CACHE) unset(OPENSSL_SSL_LIBRARY CACHE) unset(OPENSSL_CRYPTO_LIBRARY CACHE) find_package(OpenSSL REQUIRED) +set(IOTDB_NTLS_RUNTIME_LIBRARIES + "${OPENSSL_SSL_LIBRARY};${OPENSSL_CRYPTO_LIBRARY}" + CACHE INTERNAL "NTLS provider runtime libraries" FORCE) message(STATUS "[Tongsuo] built from source (shared) at ${OPENSSL_ROOT_DIR}") + +# --- Alternative provider: use preinstalled GmSSL 3 through its native TLCP API --- +elseif(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + if(NOT IOTDB_GMSSL_ROOT_DIR OR NOT IS_DIRECTORY "${IOTDB_GMSSL_ROOT_DIR}") + message(FATAL_ERROR + "[GmSSL] IOTDB_GMSSL_ROOT_DIR must point to a preinstalled GmSSL 3.2") + endif() + + unset(_gmssl_include_dir CACHE) + unset(_gmssl_library CACHE) + find_path(_gmssl_include_dir gmssl/tls.h + PATHS "${IOTDB_GMSSL_ROOT_DIR}/include" NO_DEFAULT_PATH REQUIRED) + find_library(_gmssl_library NAMES gmssl libgmssl + PATHS "${IOTDB_GMSSL_ROOT_DIR}/lib" "${IOTDB_GMSSL_ROOT_DIR}/lib64" + NO_DEFAULT_PATH REQUIRED) + + include(CMakePushCheckState) + include(CheckCXXSourceCompiles) + include(CheckCXXSourceRuns) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_INCLUDES "${_gmssl_include_dir}") + set(CMAKE_REQUIRED_LIBRARIES "${_gmssl_library}") + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES ws2_32) + set(_gmssl_saved_path "$ENV{PATH}") + set(ENV{PATH} "${IOTDB_GMSSL_ROOT_DIR}/bin;$ENV{PATH}") + else() + set(_gmssl_saved_library_path "$ENV{LD_LIBRARY_PATH}") + set(ENV{LD_LIBRARY_PATH} + "${IOTDB_GMSSL_ROOT_DIR}/lib:${IOTDB_GMSSL_ROOT_DIR}/lib64:$ENV{LD_LIBRARY_PATH}") + endif() + + set(_gmssl_compile_definitions "") + macro(_iotdb_probe_gmssl_abi_definition _definition _symbol) + string(MAKE_C_IDENTIFIER + "IOTDB_GMSSL_HAS_${_definition}_${_symbol}" _probe_variable) + unset(${_probe_variable} CACHE) + check_cxx_source_compiles( + "extern \"C\" void ${_symbol}();\nint main() { ${_symbol}(); return 0; }" + ${_probe_variable}) + if(${_probe_variable}) + list(APPEND _gmssl_compile_definitions "${_definition}") + endif() + endmacro() + _iotdb_probe_gmssl_abi_definition(ENABLE_SHA1 sha1_init) + _iotdb_probe_gmssl_abi_definition(ENABLE_SHA2 sha256_init) + _iotdb_probe_gmssl_abi_definition(ENABLE_AES aes_set_encrypt_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_SECP256R1 x509_key_set_secp256r1_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_LMS x509_key_set_lms_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_XMSS x509_key_set_xmss_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_SPHINCS x509_key_set_sphincs_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_KYBER x509_key_set_kyber_key) + _iotdb_probe_gmssl_abi_definition(ENABLE_SM9 x509_key_set_sm9_sign_key) + unset(_iotdb_probe_gmssl_abi_definition) + message(STATUS "[GmSSL] detected ABI definitions: ${_gmssl_compile_definitions}") + + foreach(_definition IN LISTS _gmssl_compile_definitions) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${_definition}") + endforeach() + unset(IOTDB_GMSSL_ABI_COMPATIBLE CACHE) + check_cxx_source_runs([=[ + #include + #include + #include + #include + #if GMSSL_VERSION_NUM < 30200 || GMSSL_VERSION_NUM >= 30300 + #error "IoTDB requires GmSSL 3.2.x" + #endif + struct GuardedContext { + TLS_CTX context; + std::uint64_t canary[8]; + }; + int main() { + GuardedContext guarded{}; + std::memset(guarded.canary, 0xA5, sizeof(guarded.canary)); + if (tls_ctx_init(&guarded.context, TLS_protocol_tlcp, 1) != 1) { + return 1; + } + const int cipher = TLS_cipher_ecc_sm4_cbc_sm3; + if (tls_ctx_set_cipher_suites(&guarded.context, &cipher, 1) != 1 || + guarded.context.is_client != 1 || + guarded.context.protocol != TLS_protocol_tlcp || + guarded.context.cipher_suites_cnt != 1 || + guarded.context.cipher_suites[0] != cipher) { + tls_ctx_cleanup(&guarded.context); + return 2; + } + const std::uint64_t expected = UINT64_C(0xA5A5A5A5A5A5A5A5); + for (std::uint64_t value : guarded.canary) { + if (value != expected) { + tls_ctx_cleanup(&guarded.context); + return 3; + } + } + tls_ctx_cleanup(&guarded.context); + return 0; + } + ]=] IOTDB_GMSSL_ABI_COMPATIBLE) + if(WIN32) + set(ENV{PATH} "${_gmssl_saved_path}") + else() + set(ENV{LD_LIBRARY_PATH} "${_gmssl_saved_library_path}") + endif() + cmake_pop_check_state() + if(NOT IOTDB_GMSSL_ABI_COMPATIBLE) + message(FATAL_ERROR + "[GmSSL] headers/library ABI check failed after probing its " + "ABI-affecting ENABLE_* symbols.") + endif() + + if(NOT TARGET IoTDB::gmssl) + add_library(IoTDB::gmssl UNKNOWN IMPORTED GLOBAL) + set_target_properties(IoTDB::gmssl PROPERTIES + IMPORTED_LOCATION "${_gmssl_library}" + INTERFACE_INCLUDE_DIRECTORIES "${_gmssl_include_dir}" + INTERFACE_COMPILE_DEFINITIONS "${_gmssl_compile_definitions}") + endif() + + set(IOTDB_NTLS_RUNTIME_LIBRARIES + "${_gmssl_library}" + CACHE INTERNAL "NTLS provider runtime libraries" FORCE) + message(STATUS "[GmSSL] using native GmSSL TLCP library ${_gmssl_library}") +endif() diff --git a/iotdb-client/client-cpp/cmake/FetchThrift.cmake b/iotdb-client/client-cpp/cmake/FetchThrift.cmake index 3cce6a87a2b4..c543ec17814e 100644 --- a/iotdb-client/client-cpp/cmake/FetchThrift.cmake +++ b/iotdb-client/client-cpp/cmake/FetchThrift.cmake @@ -153,7 +153,7 @@ else() "-DCMAKE_CXX_FLAGS=${_thrift_cxxflags}") endif() -if(WITH_SSL) +if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") list(APPEND _thrift_cmake_args "-DWITH_OPENSSL=ON") # Build Thrift's TSSLSocket against the same SSL library that iotdb_session links # and bundles, so the runtime libraries match. find_package does not set @@ -164,6 +164,15 @@ if(WITH_SSL) get_filename_component(_thrift_ossl_root "${OPENSSL_INCLUDE_DIR}" DIRECTORY) list(APPEND _thrift_cmake_args "-DOPENSSL_ROOT_DIR=${_thrift_ossl_root}") endif() + if(OPENSSL_INCLUDE_DIR) + list(APPEND _thrift_cmake_args "-DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR}") + endif() + if(OPENSSL_SSL_LIBRARY) + list(APPEND _thrift_cmake_args "-DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY}") + endif() + if(OPENSSL_CRYPTO_LIBRARY) + list(APPEND _thrift_cmake_args "-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY}") + endif() else() list(APPEND _thrift_cmake_args "-DWITH_OPENSSL=OFF") endif() @@ -181,12 +190,17 @@ endif() # Encode WITH_SSL in the stamp: toggling SSL changes WITH_OPENSSL, so a cached # build of the opposite flavour must not be reused (otherwise TSSLSocket is # missing/extra at link time). -if(WITH_SSL) +if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") set(_thrift_ssl_stamp "-ssl") else() set(_thrift_ssl_stamp "-nossl") endif() -set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_VERSION}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}-sslctx-tongsuo") +set(_thrift_provider_signature + "${IOTDB_NTLS_PROVIDER};${IOTDB_GMSSL_ROOT_DIR};${OPENSSL_ROOT_DIR};" + "${OPENSSL_SSL_LIBRARY};${OPENSSL_CRYPTO_LIBRARY}") +string(JOIN "" _thrift_provider_signature ${_thrift_provider_signature}) +string(MD5 _thrift_provider_stamp "${_thrift_provider_signature}") +set(_thrift_stamp "${_thrift_build}/.built-${THRIFT_VERSION}-${_thrift_build_config}-mdll${_thrift_abi_stamp}${_thrift_ssl_stamp}-sslctx-${_thrift_provider_stamp}") if(NOT EXISTS "${_thrift_stamp}") file(MAKE_DIRECTORY "${_thrift_build}") message(STATUS "[Thrift] configuring ${_thrift_dirname}") diff --git a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake index f3e181b8e8ff..e3073ff59cfc 100644 --- a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake +++ b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake @@ -39,7 +39,10 @@ function(_iotdb_collect_openssl_windows_dlls _out_var) if(OPENSSL_ROOT_DIR) list(APPEND _roots "${OPENSSL_ROOT_DIR}") endif() - foreach(_implib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY OPENSSL_LIBRARIES) + if(IOTDB_GMSSL_ROOT_DIR) + list(APPEND _roots "${IOTDB_GMSSL_ROOT_DIR}") + endif() + foreach(_implib IN LISTS IOTDB_NTLS_RUNTIME_LIBRARIES OPENSSL_LIBRARIES) if(_implib AND EXISTS "${_implib}") # Walk up from the import lib (.../lib, .../lib/VC/x64/MD, ...) to find # a directory that owns a bin/ holding the DLLs. @@ -60,8 +63,12 @@ function(_iotdb_collect_openssl_windows_dlls _out_var) file(GLOB _found "${_root}/bin/libssl-${OPENSSL_VERSION_MAJOR}*.dll" "${_root}/bin/libcrypto-${OPENSSL_VERSION_MAJOR}*.dll" + "${_root}/bin/gmssl*.dll" + "${_root}/bin/libgmssl*.dll" "${_root}/libssl-${OPENSSL_VERSION_MAJOR}*.dll" - "${_root}/libcrypto-${OPENSSL_VERSION_MAJOR}*.dll") + "${_root}/libcrypto-${OPENSSL_VERSION_MAJOR}*.dll" + "${_root}/gmssl*.dll" + "${_root}/libgmssl*.dll") # The same DLL can appear under several candidate roots (e.g. bin/ and # the install root); keep only the first occurrence of each filename. foreach(_dll IN LISTS _found) @@ -99,7 +106,7 @@ function(iotdb_install_openssl_runtime) # are skipped: they are already linked into libiotdb_session. set(_files_arg "") set(_have_libs OFF) - foreach(_lib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY) + foreach(_lib IN LISTS IOTDB_NTLS_RUNTIME_LIBRARIES) if(_lib AND EXISTS "${_lib}" AND NOT _lib MATCHES "\\.a$") string(APPEND _files_arg " \"${_lib}\"") set(_have_libs ON) diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index 9f2fd49e1b77..20f4da09ecf1 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -51,7 +51,9 @@ OFF ON 0.24.0 + TONGSUO 8.4-stable + ON @@ -116,7 +118,9 @@ + + diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md index 1839f8076073..44c903d3a021 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md @@ -33,7 +33,8 @@ the [`NOTICE`](NOTICE) file in this directory; non-Apache license texts are unde | --- | --- | --- | --- | | Apache Thrift | 0.24.0 | statically linked | Apache License 2.0 | | Boost | 1.60.0 on Linux/Windows, 1.84.0 on macOS by default | statically linked (header-only) | Boost Software License 1.0 | -| Tongsuo | 8.4-stable (always built from source when `WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 | +| Tongsuo | 8.4-stable (default NTLS provider) | bundled shared libs in `lib/` | Apache License 2.0 | +| GmSSL | 3.2.x (optional NTLS provider) | bundled shared library in `lib/` | Apache License 2.0 | ## Build-time only (not redistributed) diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE index 39bb234d1b44..dc01d0557fcc 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/NOTICE @@ -26,6 +26,11 @@ Licensed under the Apache License, Version 2.0 (see the top-level LICENSE). Tongsuo is an OpenSSL-compatible cryptographic library with additional Chinese commercial cipher and TLCP protocol support. +------------------------------------------------------------------------------ +GmSSL (optional NTLS provider; bundled shared library: libgmssl) +Copyright 2014-2026 The GmSSL Project. All Rights Reserved. +Licensed under the Apache License, Version 2.0 (see the top-level LICENSE). + ------------------------------------------------------------------------------ Boost C++ Libraries (header-only; used at build time to compile Apache Thrift and the iotdb_session library, so portions may be inlined into the shipped diff --git a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h index 6217a2e73761..bf19900eb5b6 100644 --- a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h @@ -62,6 +62,9 @@ class AbstractSessionBuilder { std::string trustStorePwd; std::string keyStore; std::string keyStorePwd; + std::string tlcpCertChainFile; + std::string tlcpPrivateKeyFile; + std::string tlcpPrivateKeyPwd; }; #endif // IOTDB_ABSTRACTSESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/include/SessionBuilder.h b/iotdb-client/client-cpp/src/include/SessionBuilder.h index 5d3eabd7434e..c67ec28dd60e 100644 --- a/iotdb-client/client-cpp/src/include/SessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/SessionBuilder.h @@ -69,6 +69,21 @@ class SessionBuilder : public AbstractSessionBuilder { return this; } + SessionBuilder* tlcpCertChainFile(const std::string& path) { + AbstractSessionBuilder::tlcpCertChainFile = path; + return this; + } + + SessionBuilder* tlcpPrivateKeyFile(const std::string& path) { + AbstractSessionBuilder::tlcpPrivateKeyFile = path; + return this; + } + + SessionBuilder* tlcpPrivateKeyPwd(const std::string& password) { + AbstractSessionBuilder::tlcpPrivateKeyPwd = password; + return this; + } + SessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/include/SessionC.h b/iotdb-client/client-cpp/src/include/SessionC.h index bad2b63710b8..4bb6e28f8e75 100644 --- a/iotdb-client/client-cpp/src/include/SessionC.h +++ b/iotdb-client/client-cpp/src/include/SessionC.h @@ -136,6 +136,8 @@ TsStatus ts_session_set_ssl_protocol(CSession* session, const char* sslProtocol) TsStatus ts_session_set_trust_store(CSession* session, const char* trustStore, const char* trustStorePwd); TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, const char* keyStorePwd); +TsStatus ts_session_set_tlcp_pem_files(CSession* session, const char* certChainFile, + const char* privateKeyFile, const char* privateKeyPwd); /** @deprecated Use ts_session_set_trust_store() instead. */ TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath); @@ -162,6 +164,8 @@ TsStatus ts_table_session_set_trust_store(CTableSession* session, const char* tr const char* trustStorePwd); TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyStore, const char* keyStorePwd); +TsStatus ts_table_session_set_tlcp_pem_files(CTableSession* session, const char* certChainFile, + const char* privateKeyFile, const char* privateKeyPwd); /** @deprecated Use ts_table_session_set_trust_store() instead. */ TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, const char* trustCertFilePath); diff --git a/iotdb-client/client-cpp/src/include/SessionPool.h b/iotdb-client/client-cpp/src/include/SessionPool.h index c71580262446..631216fbf81f 100644 --- a/iotdb-client/client-cpp/src/include/SessionPool.h +++ b/iotdb-client/client-cpp/src/include/SessionPool.h @@ -193,6 +193,9 @@ class SessionPool { SessionPool& setTrustStorePwd(std::string trustStorePwd); SessionPool& setKeyStore(std::string keyStore); SessionPool& setKeyStorePwd(std::string keyStorePwd); + SessionPool& setTlcpCertChainFile(std::string path); + SessionPool& setTlcpPrivateKeyFile(std::string path); + SessionPool& setTlcpPrivateKeyPwd(std::string password); // Borrow a Session. Blocks until one is free or a new one can be created, // up to timeoutMs (<= 0 means use the pool default). Throws IoTDBException on @@ -259,6 +262,9 @@ class SessionPool { std::string trustStorePwd_; std::string keyStore_; std::string keyStorePwd_; + std::string tlcpCertChainFile_; + std::string tlcpPrivateKeyFile_; + std::string tlcpPrivateKeyPwd_; // pool sizing / waiting policy size_t maxSize_; @@ -369,6 +375,18 @@ class SessionPoolBuilder : public AbstractSessionBuilder { AbstractSessionBuilder::keyStorePwd = v; return this; } + SessionPoolBuilder* tlcpCertChainFile(const std::string& v) { + AbstractSessionBuilder::tlcpCertChainFile = v; + return this; + } + SessionPoolBuilder* tlcpPrivateKeyFile(const std::string& v) { + AbstractSessionBuilder::tlcpPrivateKeyFile = v; + return this; + } + SessionPoolBuilder* tlcpPrivateKeyPwd(const std::string& v) { + AbstractSessionBuilder::tlcpPrivateKeyPwd = v; + return this; + } SessionPoolBuilder* maxSize(size_t v) { maxSize_ = v; return this; @@ -415,7 +433,10 @@ class SessionPoolBuilder : public AbstractSessionBuilder { .setTrustStore(AbstractSessionBuilder::trustStore) .setTrustStorePwd(AbstractSessionBuilder::trustStorePwd) .setKeyStore(AbstractSessionBuilder::keyStore) - .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd); + .setKeyStorePwd(AbstractSessionBuilder::keyStorePwd) + .setTlcpCertChainFile(AbstractSessionBuilder::tlcpCertChainFile) + .setTlcpPrivateKeyFile(AbstractSessionBuilder::tlcpPrivateKeyFile) + .setTlcpPrivateKeyPwd(AbstractSessionBuilder::tlcpPrivateKeyPwd); return pool; } diff --git a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h index 0642acf0759b..687bb67d56ee 100644 --- a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h +++ b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h @@ -80,6 +80,21 @@ class TableSessionBuilder : public AbstractSessionBuilder { return this; } + TableSessionBuilder* tlcpCertChainFile(const std::string& path) { + AbstractSessionBuilder::tlcpCertChainFile = path; + return this; + } + + TableSessionBuilder* tlcpPrivateKeyFile(const std::string& path) { + AbstractSessionBuilder::tlcpPrivateKeyFile = path; + return this; + } + + TableSessionBuilder* tlcpPrivateKeyPwd(const std::string& password) { + AbstractSessionBuilder::tlcpPrivateKeyPwd = password; + return this; + } + TableSessionBuilder* username(const std::string& username) { AbstractSessionBuilder::username = username; return this; diff --git a/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.cpp b/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.cpp new file mode 100644 index 000000000000..fb640187ea3d --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.cpp @@ -0,0 +1,197 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "GmsslTlcpSocket.h" + +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) + +#include +#include + +#include +#include +#include + +using apache::thrift::transport::TTransportException; + +GmsslTlcpSocket::GmsslTlcpSocket(const std::string& host, int port, SslConfig config) + : TSocket(host, port), sslConfig_(std::move(config)) { + RpcSslUtils::validateGmsslTlcpConfig(sslConfig_); +} + +GmsslTlcpSocket::~GmsslTlcpSocket() { + close(); +} + +void GmsslTlcpSocket::open() { + if (connTimeout_ > 0) { + if (recvTimeout_ <= 0) { + setRecvTimeout(connTimeout_); + } + if (sendTimeout_ <= 0) { + setSendTimeout(connTimeout_); + } + } + TSocket::open(); + try { + int result = tls_ctx_init(&tlsContext_, TLS_protocol_tlcp, 1); + if (result != 1) { + throwTransportError("GmSSL TLCP context initialization", result); + } + contextInitialized_ = true; + RpcSslUtils::configureGmsslTlcpContext(&tlsContext_, sslConfig_); + result = tls_init(&tlsConnection_, &tlsContext_); + if (result != 1) { + throwTransportError("GmSSL TLCP connection initialization", result); + } + connectionInitialized_ = true; + result = tls_set_hostname(&tlsConnection_, getHost().c_str()); + if (result != 1) { + throwTransportError("GmSSL TLCP hostname configuration", result); + } + result = tls_set_socket(&tlsConnection_, getSocketFD()); + if (result != 1) { + throwTransportError("GmSSL TLCP socket configuration", result); + } + result = tls_do_handshake(&tlsConnection_); + if (result != 1) { + throwTransportError("GmSSL TLCP handshake", result); + } + handshakeComplete_ = true; + } catch (...) { + close(); + throw; + } +} + +void GmsslTlcpSocket::close() { + if (connectionInitialized_) { + if (handshakeComplete_) { + (void)tls_shutdown(&tlsConnection_); + } + tls_client_verify_cleanup(&tlsConnection_.client_verify_ctx); + tls_cleanup(&tlsConnection_); + connectionInitialized_ = false; + handshakeComplete_ = false; + hasPeekedByte_ = false; + } + if (contextInitialized_) { + tls_ctx_cleanup(&tlsContext_); + contextInitialized_ = false; + } + TSocket::close(); +} + +bool GmsslTlcpSocket::peek() { + if (hasPeekedByte_) { + return true; + } + if (!handshakeComplete_) { + throw TTransportException(TTransportException::NOT_OPEN, "GmSSL TLCP socket is not open"); + } + size_t received = 0; + const int result = tls_recv(&tlsConnection_, &peekedByte_, 1, &received); + if (result == 1 && received == 1) { + hasPeekedByte_ = true; + return true; + } + if (result == 0 || result == TLS_ERROR_TCP_CLOSED) { + return false; + } + throwTransportError("GmSSL TLCP peek", result); +} + +uint32_t GmsslTlcpSocket::read(uint8_t* buf, uint32_t len) { + if (!handshakeComplete_) { + throw TTransportException(TTransportException::NOT_OPEN, "GmSSL TLCP socket is not open"); + } + if (len == 0) { + return 0; + } + if (hasPeekedByte_) { + buf[0] = peekedByte_; + hasPeekedByte_ = false; + return 1; + } + size_t received = 0; + const int result = tls_recv(&tlsConnection_, buf, len, &received); + if (result == 1) { + return static_cast(received); + } + if (result == 0 || result == TLS_ERROR_TCP_CLOSED) { + return 0; + } + throwTransportError("GmSSL TLCP read", result); +} + +void GmsslTlcpSocket::write(const uint8_t* buf, uint32_t len) { + uint32_t written = 0; + while (written < len) { + written += write_partial(buf + written, len - written); + } +} + +uint32_t GmsslTlcpSocket::write_partial(const uint8_t* buf, uint32_t len) { + if (!handshakeComplete_) { + throw TTransportException(TTransportException::NOT_OPEN, "GmSSL TLCP socket is not open"); + } + size_t sent = 0; + const int result = tls_send(&tlsConnection_, buf, len, &sent); + if (result == 1 && sent > 0) { + return static_cast(sent); + } + throwTransportError("GmSSL TLCP write", result); +} + +void GmsslTlcpSocket::throwTransportError(const std::string& operation, int result) const { + const int socketError = tls_socket_get_error(); + const bool isRead = operation.find("read") != std::string::npos || + operation.find("peek") != std::string::npos || + operation.find("handshake") != std::string::npos; + const tls_socket_err_t socketErrorType = tls_socket_get_error_type(socketError, isRead ? 1 : 0); + std::ostringstream message; + message << operation << " failed (GmSSL result=" << result; + if (result == TLS_ERROR_RECV_AGAIN) { + message << "/want-read"; + } else if (result == TLS_ERROR_SEND_AGAIN) { + message << "/want-write"; + } else if (result == TLS_ERROR_TCP_CLOSED) { + message << "/tcp-closed"; + } else if (result == TLS_ERROR_SYSCALL) { + message << "/syscall"; + } + if (tlsConnection_.protocol != 0) { + message << ", protocol=" << tls_protocol_name(tlsConnection_.protocol); + } + if (tlsConnection_.cipher_suite != 0) { + message << ", cipher=" << tls_cipher_suite_name(tlsConnection_.cipher_suite); + } + message << ", handshake_state=" << tlsConnection_.handshake_state + << ", send_state=" << tlsConnection_.send_state + << ", recv_state=" << tlsConnection_.recv_state + << ", verify_result=" << tlsConnection_.verify_result << ", socket_error=" << socketError + << "/" << tls_socket_get_error_string(socketError) << ", gmssl=" << gmssl_version_str() + << "). GmSSL writes its detailed error trace to stderr."; + throw TTransportException(socketErrorType == TLS_SOCKET_ERR_TIMEOUT + ? TTransportException::TIMED_OUT + : TTransportException::UNKNOWN, + message.str()); +} + +#endif diff --git a/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.h b/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.h new file mode 100644 index 000000000000..1124492e540a --- /dev/null +++ b/iotdb-client/client-cpp/src/rpc/GmsslTlcpSocket.h @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_GMSSL_TLCP_SOCKET_H +#define IOTDB_GMSSL_TLCP_SOCKET_H + +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) + +#include "RpcSslUtils.h" + +#include +#include + +#include +#include + +class GmsslTlcpSocket : public apache::thrift::transport::TSocket { +public: + GmsslTlcpSocket(const std::string& host, int port, SslConfig config); + ~GmsslTlcpSocket() override; + + void open() override; + void close() override; + bool peek() override; + uint32_t read(uint8_t* buf, uint32_t len) override; + void write(const uint8_t* buf, uint32_t len) override; + uint32_t write_partial(const uint8_t* buf, uint32_t len) override; + +private: + [[noreturn]] void throwTransportError(const std::string& operation, int result) const; + + SslConfig sslConfig_; + TLS_CTX tlsContext_{}; + TLS_CONNECT tlsConnection_{}; + uint8_t peekedByte_ = 0; + bool hasPeekedByte_ = false; + bool contextInitialized_ = false; + bool connectionInitialized_ = false; + bool handshakeComplete_ = false; +}; + +#endif + +#endif // IOTDB_GMSSL_TLCP_SOCKET_H diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp index 3e47ad71a32b..ea97e49a3be6 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -26,7 +26,7 @@ #endif #endif -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) #include #include #include @@ -34,6 +34,8 @@ #include #include #include +#elif WITH_SSL && defined(IOTDB_NTLS_PROVIDER_GMSSL) +#include #endif #include "RpcSslUtils.h" @@ -44,6 +46,7 @@ #include #include #include +#include #include namespace { @@ -84,6 +87,7 @@ bool isPkcs12Path(const std::string& path) { #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) std::string collectOpenSslErrors() { std::string errors; unsigned long errCode = 0; @@ -101,6 +105,7 @@ std::string collectOpenSslErrors() { void throwSslError(const std::string& message) { throw IoTDBException(message + ": " + collectOpenSslErrors()); } +#endif void ensureFileReadable(const std::string& path, const std::string& label) { if (!hasText(path)) { @@ -112,6 +117,8 @@ void ensureFileReadable(const std::string& path, const std::string& label) { } } +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) + PKCS12* loadPkcs12(const std::string& path, const std::string& password) { BIO* bio = BIO_new_file(path.c_str(), "rb"); if (bio == nullptr) { @@ -472,6 +479,89 @@ void loadTlcpKeyStoreFromPkcs12(SSL_CTX* ctx, const std::string& path, freeTlcpIdentity(identity); } +void loadTlcpIdentityFromPemBundles(SSL_CTX* ctx, const std::string& certificateChainFile, + const std::string& privateKeyFile, + const std::string& privateKeyPassword) { + ensureFileReadable(certificateChainFile, "TLCP certificate chain"); + ensureFileReadable(privateKeyFile, "TLCP private key"); + + BIO* certBio = BIO_new_file(certificateChainFile.c_str(), "rb"); + if (certBio == nullptr) { + throwSslError("Failed to open TLCP certificate chain " + certificateChainFile); + } + X509* signCert = PEM_read_bio_X509(certBio, nullptr, nullptr, nullptr); + X509* encCert = PEM_read_bio_X509(certBio, nullptr, nullptr, nullptr); + std::vector certificateChain; + while (true) { + X509* chainCert = PEM_read_bio_X509(certBio, nullptr, nullptr, nullptr); + if (chainCert == nullptr) { + ERR_clear_error(); + break; + } + certificateChain.push_back(chainCert); + } + BIO_free(certBio); + if (signCert == nullptr || encCert == nullptr) { + X509_free(signCert); + X509_free(encCert); + for (X509* cert : certificateChain) { + X509_free(cert); + } + throw IoTDBException( + "TLCP certificate-chain PEM must contain signing and encryption certificates: " + + certificateChainFile); + } + + BIO* keyBio = BIO_new_file(privateKeyFile.c_str(), "rb"); + if (keyBio == nullptr) { + X509_free(signCert); + X509_free(encCert); + for (X509* cert : certificateChain) { + X509_free(cert); + } + throwSslError("Failed to open TLCP private-key bundle " + privateKeyFile); + } + void* password = + privateKeyPassword.empty() ? nullptr : const_cast(privateKeyPassword.c_str()); + EVP_PKEY* signKey = PEM_read_bio_PrivateKey(keyBio, nullptr, nullptr, password); + EVP_PKEY* encKey = PEM_read_bio_PrivateKey(keyBio, nullptr, nullptr, password); + BIO_free(keyBio); + if (signKey == nullptr || encKey == nullptr) { + X509_free(signCert); + X509_free(encCert); + EVP_PKEY_free(signKey); + EVP_PKEY_free(encKey); + for (X509* cert : certificateChain) { + X509_free(cert); + } + throw IoTDBException("TLCP private-key PEM must contain signing and encryption private keys: " + + privateKeyFile); + } + + const bool loaded = SSL_CTX_use_sign_certificate(ctx, signCert) == 1 && + SSL_CTX_use_sign_PrivateKey(ctx, signKey) == 1 && + SSL_CTX_use_enc_certificate(ctx, encCert) == 1 && + SSL_CTX_use_enc_PrivateKey(ctx, encKey) == 1; + X509_free(signCert); + X509_free(encCert); + EVP_PKEY_free(signKey); + EVP_PKEY_free(encKey); + if (!loaded) { + for (X509* cert : certificateChain) { + X509_free(cert); + } + throwSslError("Failed to load TLCP PEM client credentials"); + } + for (size_t index = 0; index < certificateChain.size(); ++index) { + if (SSL_CTX_add_extra_chain_cert(ctx, certificateChain[index]) != 1) { + for (size_t remaining = index; remaining < certificateChain.size(); ++remaining) { + X509_free(certificateChain[remaining]); + } + throwSslError("Failed to load TLCP PEM client certificate chain"); + } + } +} + void applyTlsProtocolVersion(SSL_CTX* ctx, const std::string& protocol) { const std::string resolved = RpcSslUtils::normalizeProtocol(protocol); const std::string upper = toUpper(resolved); @@ -510,27 +600,40 @@ SSL_CTX* createTlsClientContext(const SslConfig& config) { } SSL_CTX* createTlcpClientContext(const SslConfig& config) { - SSL_CTX* ctx = SSL_CTX_new(NTLS_client_method()); + std::unique_ptr ctx(SSL_CTX_new(NTLS_client_method()), + SSL_CTX_free); if (ctx == nullptr) { throwSslError("Failed to create TLCP client context"); } - SSL_CTX_enable_ntls(ctx); - if (SSL_CTX_set_cipher_list(ctx, RpcSslUtils::DEFAULT_TLCP_CIPHER) != 1) { - SSL_CTX_free(ctx); + SSL_CTX_enable_ntls(ctx.get()); + if (SSL_CTX_set_cipher_list(ctx.get(), RpcSslUtils::DEFAULT_TLCP_CIPHER) != 1) { throwSslError("Failed to set TLCP cipher suite"); } const std::string trustStore = config.effectiveTrustStore(); if (hasText(trustStore)) { - loadTrustStore(ctx, trustStore, config.trustStorePwd); - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + loadTrustStore(ctx.get(), trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr); } else { - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, nullptr); } if (hasText(config.keyStore)) { - loadTlcpKeyStoreFromPkcs12(ctx, config.keyStore, config.keyStorePwd); + loadTlcpKeyStoreFromPkcs12(ctx.get(), config.keyStore, config.keyStorePwd); } - return ctx; + const bool hasCertificate = hasText(config.tlcpCertChainFile); + const bool hasPrivateKey = hasText(config.tlcpPrivateKeyFile); + if (hasCertificate != hasPrivateKey) { + throw IoTDBException( + "Mutual TLCP authentication requires both certificate-chain and private-key PEM files."); + } + if (hasText(config.keyStore) && hasCertificate) { + throw IoTDBException("Configure either a TLCP PKCS12 key store or PEM credentials, not both."); + } + if (hasCertificate) { + loadTlcpIdentityFromPemBundles(ctx.get(), config.tlcpCertChainFile, config.tlcpPrivateKeyFile, + config.tlcpPrivateKeyPwd); + } + return ctx.release(); } void validatePkcs12Store(const std::string& path, const std::string& password) { @@ -583,6 +686,46 @@ void validatePemStore(const std::string& path) { } } +#elif defined(IOTDB_NTLS_PROVIDER_GMSSL) + +void configureGmsslTlcpContextImpl(TLS_CTX* ctx, const SslConfig& config) { + const int cipherSuite = TLS_cipher_ecc_sm4_cbc_sm3; + if (tls_ctx_set_cipher_suites(ctx, &cipherSuite, 1) != 1) { + throw IoTDBException("Failed to configure GmSSL TLCP cipher suite"); + } + + const std::string trustStore = config.effectiveTrustStore(); + if (hasText(trustStore)) { + ensureFileReadable(trustStore, "Trust store"); + if (isPkcs12Path(trustStore)) { + throw IoTDBException("The GmSSL provider requires a PEM trust store: " + trustStore); + } + if (tls_ctx_set_ca_certificates(ctx, trustStore.c_str(), TLS_DEFAULT_VERIFY_DEPTH) != 1) { + throw IoTDBException("Failed to load GmSSL PEM trust store " + trustStore); + } + } + + const bool hasCertificate = hasText(config.tlcpCertChainFile); + const bool hasPrivateKey = hasText(config.tlcpPrivateKeyFile); + if (hasCertificate != hasPrivateKey) { + throw IoTDBException( + "GmSSL mutual TLCP authentication requires both certificate-chain and private-key PEM " + "files."); + } + if (hasCertificate) { + ensureFileReadable(config.tlcpCertChainFile, "TLCP certificate chain"); + ensureFileReadable(config.tlcpPrivateKeyFile, "TLCP private key"); + const char* password = + config.tlcpPrivateKeyPwd.empty() ? nullptr : config.tlcpPrivateKeyPwd.c_str(); + if (tls_ctx_set_certificate_and_key(ctx, config.tlcpCertChainFile.c_str(), + config.tlcpPrivateKeyFile.c_str(), password) != 1) { + throw IoTDBException("Failed to load GmSSL TLCP client credentials"); + } + } +} + +#endif + #endif // WITH_SSL } // namespace @@ -620,11 +763,18 @@ void RpcSslUtils::validateTrustStore(const std::string& trustStorePath, const std::string& trustStorePassword) { #if WITH_SSL ensureFileReadable(trustStorePath, "Trust store"); +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) if (isPkcs12Path(trustStorePath)) { validatePkcs12Store(trustStorePath, trustStorePassword); } else { validatePemStore(trustStorePath); } +#else + (void)trustStorePassword; + if (isPkcs12Path(trustStorePath)) { + throw IoTDBException("The GmSSL provider requires a PEM trust store: " + trustStorePath); + } +#endif #else (void)trustStorePath; (void)trustStorePassword; @@ -636,11 +786,18 @@ void RpcSslUtils::validateKeyStore(const std::string& keyStorePath, const std::string& keyStorePassword) { #if WITH_SSL ensureFileReadable(keyStorePath, "Key store"); +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) if (isPkcs12Path(keyStorePath)) { validatePkcs12Store(keyStorePath, keyStorePassword); } else { validatePemStore(keyStorePath); } +#else + (void)keyStorePassword; + if (isPkcs12Path(keyStorePath)) { + throw IoTDBException("The GmSSL provider requires PEM client credentials: " + keyStorePath); + } +#endif #else (void)keyStorePath; (void)keyStorePassword; @@ -650,6 +807,7 @@ void RpcSslUtils::validateKeyStore(const std::string& keyStorePath, #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) SSL_CTX* RpcSslUtils::createClientSslContext(const SslConfig& config) { const std::string protocol = resolveProtocol(config.sslProtocol); if (isTlcpProtocol(protocol)) { @@ -669,5 +827,25 @@ RpcSslUtils::createSslSocketFactory(const SslConfig& config) { factory->authenticate(false); return factory; } +#elif defined(IOTDB_NTLS_PROVIDER_GMSSL) +void RpcSslUtils::validateGmsslTlcpConfig(const SslConfig& config) { + if (!isTlcpProtocol(resolveProtocol(config.sslProtocol))) { + throw IoTDBException("The GmSSL provider supports TLCP only; configure sslProtocol as TLCP."); + } + if (hasText(config.keyStore)) { + throw IoTDBException( + "The GmSSL provider does not support PKCS12 keyStore; configure TLCP PEM certificate and " + "private-key files."); + } +} + +void RpcSslUtils::configureGmsslTlcpContext(TLS_CTX* context, const SslConfig& config) { + if (context == nullptr) { + throw IoTDBException("GmSSL TLCP context must not be null."); + } + validateGmsslTlcpConfig(config); + configureGmsslTlcpContextImpl(context, config); +} +#endif #endif diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h index f164892be928..8a3d9d345bc1 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h @@ -24,8 +24,12 @@ #include #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) #include #include +#elif defined(IOTDB_NTLS_PROVIDER_GMSSL) +#include +#endif #endif struct SslConfig { @@ -35,6 +39,11 @@ struct SslConfig { std::string trustStorePwd; std::string keyStore; std::string keyStorePwd; + /** TLCP PEM client certificate chain; provider-specific ordering is documented in README. */ + std::string tlcpCertChainFile; + /** TLCP PEM client private key bundle; provider-specific contents are documented in README. */ + std::string tlcpPrivateKeyFile; + std::string tlcpPrivateKeyPwd; /** Legacy PEM trust certificate path; used when trustStore is empty. */ std::string trustCertFilePath; @@ -59,9 +68,14 @@ class RpcSslUtils { const std::string& keyStorePassword); #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_TONGSUO) static SSL_CTX* createClientSslContext(const SslConfig& config); static std::shared_ptr createSslSocketFactory(const SslConfig& config); +#elif defined(IOTDB_NTLS_PROVIDER_GMSSL) + static void validateGmsslTlcpConfig(const SslConfig& config); + static void configureGmsslTlcpContext(TLS_CTX* context, const SslConfig& config); +#endif #endif }; diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp index d9a78b06c1ad..77afa6e018a6 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp @@ -17,6 +17,9 @@ * under the License. */ #include "SessionConnection.h" +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) +#include "GmsslTlcpSocket.h" +#endif #include "SessionImpl.h" #include "RpcSslUtils.h" #include "RpcCommon.h" @@ -96,8 +99,12 @@ SessionConnection::~SessionConnection() { void SessionConnection::init(const TEndPoint& endpoint, const SslConfig& sslConfig) { if (sslConfig.useSsl) { #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) + auto sslSocket = std::make_shared(endPoint.ip, endPoint.port, sslConfig); +#else socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint.ip, endPoint.port); +#endif sslSocket->setConnTimeout(connectionTimeoutInMs); transport = std::make_shared(sslSocket); #else diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.h b/iotdb-client/client-cpp/src/rpc/SessionConnection.h index 5216c96bd803..f19bb07ef8dd 100644 --- a/iotdb-client/client-cpp/src/rpc/SessionConnection.h +++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.h @@ -23,7 +23,7 @@ #include #include #include -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) #include #endif @@ -183,7 +183,7 @@ class SessionConnection : public std::enable_shared_from_this TSStatus insertTabletsInternal(TSInsertTabletsReq request); TSStatus deleteDataInternal(TSDeleteDataReq request); -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) std::shared_ptr socketFactory_ = std::make_shared(); #endif diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp index c2a173865d34..dcab419f813e 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp @@ -17,6 +17,9 @@ * under the License. */ #include "ThriftConnection.h" +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) +#include "GmsslTlcpSocket.h" +#endif #include "RpcSslUtils.h" #include #include @@ -69,8 +72,12 @@ void ThriftConnection::init(const std::string& username, const std::string& pass const std::string& zoneId, const std::string& version) { if (sslConfig.useSsl) { #if WITH_SSL +#if defined(IOTDB_NTLS_PROVIDER_GMSSL) + auto sslSocket = std::make_shared(endPoint_.ip, endPoint_.port, sslConfig); +#else socketFactory_ = RpcSslUtils::createSslSocketFactory(sslConfig); auto sslSocket = socketFactory_->createSocket(endPoint_.ip, endPoint_.port); +#endif sslSocket->setConnTimeout(connectionTimeoutInMs_); transport_ = std::make_shared(sslSocket); #else diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h index 495b74d77dd8..70fb1961d7a1 100644 --- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h +++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h @@ -20,7 +20,7 @@ #define IOTDB_THRIFTCONNECTION_H #include -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) #include #endif #include "IClientRPCService.h" @@ -60,7 +60,7 @@ class ThriftConnection { int connectionTimeoutInMs_; int fetchSize_; -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) std::shared_ptr socketFactory_ = std::make_shared(); #endif diff --git a/iotdb-client/client-cpp/src/session/Session.cpp b/iotdb-client/client-cpp/src/session/Session.cpp index 458a50548452..ca9421f3921d 100644 --- a/iotdb-client/client-cpp/src/session/Session.cpp +++ b/iotdb-client/client-cpp/src/session/Session.cpp @@ -639,6 +639,9 @@ Session::Session(AbstractSessionBuilder* builder) : impl_(new Impl()) { impl_->sslConfig_.trustStorePwd = builder->trustStorePwd; impl_->sslConfig_.keyStore = builder->keyStore; impl_->sslConfig_.keyStorePwd = builder->keyStorePwd; + impl_->sslConfig_.tlcpCertChainFile = builder->tlcpCertChainFile; + impl_->sslConfig_.tlcpPrivateKeyFile = builder->tlcpPrivateKeyFile; + impl_->sslConfig_.tlcpPrivateKeyPwd = builder->tlcpPrivateKeyPwd; impl_->sslConfig_.trustCertFilePath = builder->trustCertFilePath; impl_->initZoneId(); impl_->initNodesSupplier(impl_->nodeUrls_); diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp b/iotdb-client/client-cpp/src/session/SessionC.cpp index edf710a91111..da64f0264673 100644 --- a/iotdb-client/client-cpp/src/session/SessionC.cpp +++ b/iotdb-client/client-cpp/src/session/SessionC.cpp @@ -417,6 +417,28 @@ TsStatus ts_session_set_key_store(CSession* session, const char* keyStore, return TS_OK; } +TsStatus ts_session_set_tlcp_pem_files(CSession* session, const char* certChainFile, + const char* privateKeyFile, const char* privateKeyPwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = + setSslStringField(session->sslConfig.tlcpCertChainFile, certChainFile, "certChainFile"); + if (status != TS_OK) { + return status; + } + status = + setSslStringField(session->sslConfig.tlcpPrivateKeyFile, privateKeyFile, "privateKeyFile"); + if (status != TS_OK) { + return status; + } + if (privateKeyPwd != nullptr) { + session->sslConfig.tlcpPrivateKeyPwd = privateKeyPwd; + } + session->sslConfigured = true; + return TS_OK; +} + TsStatus ts_session_set_trust_cert_file_path(CSession* session, const char* trustCertFilePath) { clearError(); if (!session) @@ -553,6 +575,29 @@ TsStatus ts_table_session_set_key_store(CTableSession* session, const char* keyS return TS_OK; } +TsStatus ts_table_session_set_tlcp_pem_files(CTableSession* session, const char* certChainFile, + const char* privateKeyFile, + const char* privateKeyPwd) { + clearError(); + if (!session) + return setError(TS_ERR_NULL_PTR, "session is null"); + TsStatus status = + setSslStringField(session->sslConfig.tlcpCertChainFile, certChainFile, "certChainFile"); + if (status != TS_OK) { + return status; + } + status = + setSslStringField(session->sslConfig.tlcpPrivateKeyFile, privateKeyFile, "privateKeyFile"); + if (status != TS_OK) { + return status; + } + if (privateKeyPwd != nullptr) { + session->sslConfig.tlcpPrivateKeyPwd = privateKeyPwd; + } + session->sslConfigured = true; + return TS_OK; +} + TsStatus ts_table_session_set_trust_cert_file_path(CTableSession* session, const char* trustCertFilePath) { clearError(); diff --git a/iotdb-client/client-cpp/src/session/SessionPool.cpp b/iotdb-client/client-cpp/src/session/SessionPool.cpp index 42961dbaff61..1f5453c994ea 100644 --- a/iotdb-client/client-cpp/src/session/SessionPool.cpp +++ b/iotdb-client/client-cpp/src/session/SessionPool.cpp @@ -134,6 +134,21 @@ SessionPool& SessionPool::setKeyStorePwd(std::string keyStorePwd) { return *this; } +SessionPool& SessionPool::setTlcpCertChainFile(std::string path) { + tlcpCertChainFile_ = std::move(path); + return *this; +} + +SessionPool& SessionPool::setTlcpPrivateKeyFile(std::string path) { + tlcpPrivateKeyFile_ = std::move(path); + return *this; +} + +SessionPool& SessionPool::setTlcpPrivateKeyPwd(std::string password) { + tlcpPrivateKeyPwd_ = std::move(password); + return *this; +} + std::shared_ptr SessionPool::constructNewSession() { AbstractSessionBuilder builder; builder.host = host_; @@ -156,6 +171,9 @@ std::shared_ptr SessionPool::constructNewSession() { builder.trustStorePwd = trustStorePwd_; builder.keyStore = keyStore_; builder.keyStorePwd = keyStorePwd_; + builder.tlcpCertChainFile = tlcpCertChainFile_; + builder.tlcpPrivateKeyFile = tlcpPrivateKeyFile_; + builder.tlcpPrivateKeyPwd = tlcpPrivateKeyPwd_; auto session = std::make_shared(&builder); session->open(enableRPCCompression_, connectTimeoutMs_); diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index f798719c3e5e..5cc7fe82acd8 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -54,18 +54,27 @@ add_executable(session_relational_tests main_Relational.cpp cpp/sessionRelat add_executable(session_c_tests main_c.cpp cpp/sessionCIT.cpp) add_executable(session_c_relational_tests main_c_Relational.cpp cpp/sessionCRelationalIT.cpp) add_executable(session_utils_tests main_utils.cpp cpp/sessionUtilsTest.cpp) -add_executable(rpc_ssl_utils_tests - main_rpc_ssl.cpp - cpp/RpcSslUtilsTest.cpp - cpp/RpcSslTlsMutualAuthTest.cpp - cpp/RpcSslIotdbE2eTest.cpp - cpp/SslTestFixtures.cpp - cpp/ItSslConnection.cpp) -add_executable(rpc_ntls_utils_tests - main_rpc_ntls.cpp - cpp/RpcSslTlcpMutualAuthTest.cpp - cpp/RpcNtlsE2eTest.cpp - cpp/SslTestFixtures.cpp) +if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + add_executable(rpc_ssl_utils_tests + main_rpc_ssl.cpp + cpp/RpcSslUtilsTest.cpp) + add_executable(rpc_ntls_utils_tests + main_rpc_ntls.cpp + cpp/RpcGmsslNtlsTest.cpp) +else() + add_executable(rpc_ssl_utils_tests + main_rpc_ssl.cpp + cpp/RpcSslUtilsTest.cpp + cpp/RpcSslTlsMutualAuthTest.cpp + cpp/RpcSslIotdbE2eTest.cpp + cpp/SslTestFixtures.cpp + cpp/ItSslConnection.cpp) + add_executable(rpc_ntls_utils_tests + main_rpc_ntls.cpp + cpp/RpcSslTlcpMutualAuthTest.cpp + cpp/RpcNtlsE2eTest.cpp + cpp/SslTestFixtures.cpp) +endif() foreach(_t IN LISTS _test_targets) target_include_directories(${_t} PRIVATE @@ -79,14 +88,31 @@ foreach(_t IN LISTS _test_targets) endif() target_link_libraries(${_t} PRIVATE iotdb_session) if(WITH_SSL) - target_link_libraries(${_t} PRIVATE OpenSSL::SSL OpenSSL::Crypto) + if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + target_link_libraries(${_t} PRIVATE IoTDB::gmssl) + else() + target_link_libraries(${_t} PRIVATE OpenSSL::SSL OpenSSL::Crypto) + endif() endif() endforeach() +foreach(_t IN LISTS _rpc_ssl_test_targets) + # Catch2 2.13.7 declares a SIGSTKSZ-sized static array, which is not a + # constant expression with newer glibc versions. + target_compile_definitions(${_t} PRIVATE CATCH_CONFIG_NO_POSIX_SIGNALS=1) +endforeach() if(WITH_SSL) - target_compile_definitions(rpc_ssl_utils_tests PRIVATE IOTDB_RPC_SSL_IT=1) + if(IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") + target_compile_definitions(rpc_ssl_utils_tests PRIVATE IOTDB_RPC_SSL_IT=1) + endif() - if(WIN32) + if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + if(WIN32) + set(_iotdb_openssl_executable "${IOTDB_GMSSL_ROOT_DIR}/bin/gmssl.exe") + else() + set(_iotdb_openssl_executable "${IOTDB_GMSSL_ROOT_DIR}/bin/gmssl") + endif() + elseif(WIN32) set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl.exe") else() set(_iotdb_openssl_executable "${OPENSSL_ROOT_DIR}/bin/openssl") @@ -112,6 +138,10 @@ if(WITH_SSL) target_compile_definitions(rpc_ntls_utils_tests PRIVATE IOTDB_OPENSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}" IOTDB_OPENSSL_ROOT_DIR="${_iotdb_openssl_root_dir_fwd}") + if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") + target_compile_definitions(rpc_ntls_utils_tests PRIVATE + IOTDB_GMSSL_EXECUTABLE="${_iotdb_openssl_executable_fwd}") + endif() if(WIN32) foreach(_t IN LISTS _rpc_ssl_test_targets) target_link_libraries(${_t} PRIVATE ws2_32 "${THRIFT_STATIC_LIB_PATH}") @@ -178,7 +208,7 @@ else() $ $ COMMENT "Copy IoTDB runtime library next to ${_t}") if(WITH_SSL AND ${_t} IN_LIST _rpc_ssl_test_targets) - foreach(_ssl_lib IN LISTS OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY) + foreach(_ssl_lib IN LISTS IOTDB_NTLS_RUNTIME_LIBRARIES) if(_ssl_lib AND EXISTS "${_ssl_lib}") add_custom_command(TARGET ${_t} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/iotdb-client/client-cpp/test/cpp/RpcGmsslNtlsTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcGmsslNtlsTest.cpp new file mode 100644 index 000000000000..f085b6779d28 --- /dev/null +++ b/iotdb-client/client-cpp/test/cpp/RpcGmsslNtlsTest.cpp @@ -0,0 +1,338 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "Common.h" +#include "GmsslTlcpSocket.h" +#include "RpcSslUtils.h" + +#if defined(_WIN32) +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include + +namespace { + +std::string fixture(const std::string& name) { + return std::string(IOTDB_TEST_FIXTURES_DIR) + "/gmssl/" + name; +} + +void initializeSockets() { +#if defined(_WIN32) + static const bool winsockInitialized = [] { + WSADATA data; + return WSAStartup(MAKEWORD(2, 2), &data) == 0; + }(); + REQUIRE(winsockInitialized); +#endif +} + +void closeSocket(tls_socket_t socket) { +#if defined(_WIN32) + closesocket(socket); +#else + close(socket); +#endif +} + +bool isValidSocket(tls_socket_t socket) { +#if defined(_WIN32) + return socket != INVALID_SOCKET; +#else + return socket >= 0; +#endif +} + +void setSocketTimeout(tls_socket_t socket) { +#if defined(_WIN32) + const DWORD timeout = 3000; + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), + sizeof(timeout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&timeout), + sizeof(timeout)); +#else + const timeval timeout{3, 0}; + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); +#endif +} + +class GmsslTestServer { +public: + explicit GmsslTestServer(bool requireClientCertificate, bool exchangeFrame = false) + : requireClientCertificate_(requireClientCertificate), exchangeFrame_(exchangeFrame) { + initializeSockets(); + listener_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + REQUIRE(isValidSocket(listener_)); + sockaddr_in address{}; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + address.sin_port = 0; + REQUIRE(::bind(listener_, reinterpret_cast(&address), sizeof(address)) == 0); + REQUIRE(listen(listener_, 1) == 0); +#if defined(_WIN32) + int length = sizeof(address); +#else + socklen_t length = sizeof(address); +#endif + REQUIRE(getsockname(listener_, reinterpret_cast(&address), &length) == 0); + port_ = ntohs(address.sin_port); + thread_ = std::thread(&GmsslTestServer::serve, this); + } + + ~GmsslTestServer() { + if (isValidSocket(listener_)) { + closeSocket(listener_); + listener_ = invalidSocket(); + } + if (thread_.joinable()) { + thread_.join(); + } + } + + int port() const { + return port_; + } + + void finish() { + if (thread_.joinable()) { + thread_.join(); + } + if (isValidSocket(listener_)) { + closeSocket(listener_); + listener_ = invalidSocket(); + } + REQUIRE(serverError_.empty()); + } + +private: + static tls_socket_t invalidSocket() { +#if defined(_WIN32) + return INVALID_SOCKET; +#else + return -1; +#endif + } + + static bool receiveAll(TLS_CONNECT* connection, uint8_t* data, size_t size) { + size_t offset = 0; + while (offset < size) { + size_t received = 0; + if (tls_recv(connection, data + offset, size - offset, &received) != 1 || received == 0) { + return false; + } + offset += received; + } + return true; + } + + static bool sendAll(TLS_CONNECT* connection, const uint8_t* data, size_t size) { + size_t offset = 0; + while (offset < size) { + size_t sent = 0; + if (tls_send(connection, data + offset, size - offset, &sent) != 1 || sent == 0) { + return false; + } + offset += sent; + } + return true; + } + + void serve() { + tls_socket_t client = accept(listener_, nullptr, nullptr); + if (!isValidSocket(client)) { + serverError_ = "accept failed"; + return; + } + setSocketTimeout(client); + + TLS_CTX context{}; + TLS_CONNECT connection{}; + bool contextInitialized = false; + bool connectionInitialized = false; + bool handshakeComplete = false; + const int cipherSuite = TLS_cipher_ecc_sm4_cbc_sm3; + if (tls_ctx_init(&context, TLS_protocol_tlcp, 0) != 1) { + serverError_ = "server context initialization failed"; + goto cleanup; + } + contextInitialized = true; + if (tls_ctx_set_cipher_suites(&context, &cipherSuite, 1) != 1 || + tls_ctx_set_tlcp_server_certificate_and_keys(&context, fixture("server-certs.pem").c_str(), + fixture("server-keys.pem").c_str(), + "thrift") != 1) { + serverError_ = "server credentials failed"; + goto cleanup; + } + if (requireClientCertificate_ && + tls_ctx_set_ca_certificates(&context, fixture("ca.crt").c_str(), + TLS_DEFAULT_VERIFY_DEPTH) != 1) { + serverError_ = "server CA configuration failed"; + goto cleanup; + } + if (tls_init(&connection, &context) != 1 || tls_set_socket(&connection, client) != 1) { + serverError_ = "server connection initialization failed"; + goto cleanup; + } + connectionInitialized = true; + if (tls_do_handshake(&connection) != 1) { + serverError_ = "server handshake failed"; + goto cleanup; + } + handshakeComplete = true; + if (exchangeFrame_) { + std::array request{}; + if (!receiveAll(&connection, request.data(), request.size()) || + request != std::array{{0, 0, 0, 4, 'p', 'i', 'n', 'g'}}) { + serverError_ = "server framed request mismatch"; + goto cleanup; + } + const std::array response{{0, 0, 0, 4, 'p', 'o', 'n', 'g'}}; + if (!sendAll(&connection, response.data(), response.size())) { + serverError_ = "server framed response failed"; + } + } + + cleanup: + if (connectionInitialized) { + if (handshakeComplete) { + (void)tls_shutdown(&connection); + } + tls_client_verify_cleanup(&connection.client_verify_ctx); + tls_cleanup(&connection); + } + if (contextInitialized) { + tls_ctx_cleanup(&context); + } + closeSocket(client); + } + + tls_socket_t listener_ = invalidSocket(); + std::thread thread_; + std::string serverError_; + bool requireClientCertificate_; + bool exchangeFrame_; + int port_; +}; + +SslConfig gmsslConfig(bool mutual) { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = fixture("ca.crt"); + if (mutual) { + config.tlcpCertChainFile = fixture("client.crt"); + config.tlcpPrivateKeyFile = fixture("client.key"); + config.tlcpPrivateKeyPwd = "thrift"; + } + return config; +} + +} // namespace + +TEST_CASE("GmSSL configures a one-way TLCP client context", "[rpc][ntls][gmssl]") { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = fixture("ca.crt"); + + TLS_CTX context{}; + REQUIRE(tls_ctx_init(&context, TLS_protocol_tlcp, 1) == 1); + REQUIRE_NOTHROW(RpcSslUtils::configureGmsslTlcpContext(&context, config)); + tls_ctx_cleanup(&context); +} + +TEST_CASE("GmSSL loads mutual TLCP PEM bundles", "[rpc][ntls][gmssl]") { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = fixture("ca.crt"); + config.tlcpCertChainFile = fixture("client.crt"); + config.tlcpPrivateKeyFile = fixture("client.key"); + config.tlcpPrivateKeyPwd = "thrift"; + + TLS_CTX context{}; + REQUIRE(tls_ctx_init(&context, TLS_protocol_tlcp, 1) == 1); + REQUIRE_NOTHROW(RpcSslUtils::configureGmsslTlcpContext(&context, config)); + tls_ctx_cleanup(&context); +} + +TEST_CASE("GmSSL rejects unsupported TLS and PKCS12 client stores", "[rpc][ntls][gmssl]") { + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + REQUIRE_THROWS_WITH(RpcSslUtils::validateGmsslTlcpConfig(config), + Catch::Contains("supports TLCP only")); + + config.sslProtocol = "TLCP"; + config.keyStore = "client.p12"; + REQUIRE_THROWS_WITH(RpcSslUtils::validateGmsslTlcpConfig(config), + Catch::Contains("does not support PKCS12 keyStore")); +} + +TEST_CASE("GmSSL native transport completes one-way TLCP handshake", "[rpc][ntls][gmssl][e2e]") { + GmsslTestServer server(false); + GmsslTlcpSocket socket("127.0.0.1", server.port(), gmsslConfig(false)); + socket.setConnTimeout(3000); + REQUIRE_NOTHROW(socket.open()); + socket.close(); + server.finish(); +} + +TEST_CASE("GmSSL native transport completes mutual TLCP handshake", "[rpc][ntls][gmssl][e2e]") { + GmsslTestServer server(true); + GmsslTlcpSocket socket("127.0.0.1", server.port(), gmsslConfig(true)); + socket.setConnTimeout(3000); + REQUIRE_NOTHROW(socket.open()); + socket.close(); + server.finish(); +} + +TEST_CASE("GmSSL transport exchanges a Thrift frame and peeks decrypted data", + "[rpc][ntls][gmssl][e2e]") { + GmsslTestServer server(false, true); + auto socket = std::make_shared("127.0.0.1", server.port(), gmsslConfig(false)); + socket->setConnTimeout(3000); + apache::thrift::transport::TFramedTransport transport(socket); + + transport.open(); + const std::array request{{'p', 'i', 'n', 'g'}}; + transport.write(request.data(), request.size()); + transport.flush(); + REQUIRE(transport.peek()); + std::array response{}; + REQUIRE(transport.readAll(response.data(), response.size()) == response.size()); + const std::array expected{{'p', 'o', 'n', 'g'}}; + REQUIRE(response == expected); + transport.close(); + server.finish(); +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp index 9bf6558f6e23..edb422c80117 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcNtlsE2eTest.cpp @@ -19,7 +19,9 @@ #include +#include #include +#include #include #include @@ -50,8 +52,37 @@ SslConfig tlcpMutualConfig() { return config; } -bool startTlcpServer(ssltest::OpenSslServerProcess& server, bool requireClientCert) { - const std::string caFile = ssltest::tlcpFixture("ca.crt"); +void concatenate(const std::string& output, std::initializer_list inputs) { + std::ofstream out(output, std::ios::binary | std::ios::trunc); + REQUIRE(out.good()); + for (const auto& input : inputs) { + std::ifstream in(input, std::ios::binary); + REQUIRE(in.good()); + out << in.rdbuf() << '\n'; + } +} + +struct IntermediatePemCredentials { + std::string certChain = "tongsuo-intermediate-client-certs.pem"; + std::string privateKeys = "tongsuo-intermediate-client-keys.pem"; + + IntermediatePemCredentials() { + concatenate(certChain, {ssltest::tlcpFixture("intermediate_client_sign.crt"), + ssltest::tlcpFixture("intermediate_client_enc.crt"), + ssltest::tlcpFixture("intermediate_ca.crt")}); + concatenate(privateKeys, {ssltest::tlcpFixture("intermediate_client_sign.key"), + ssltest::tlcpFixture("intermediate_client_enc.key")}); + } + + ~IntermediatePemCredentials() { + std::remove(certChain.c_str()); + std::remove(privateKeys.c_str()); + } +}; + +bool startTlcpServer(ssltest::OpenSslServerProcess& server, bool requireClientCert, + const std::string& clientCaFile = "") { + const std::string caFile = clientCaFile.empty() ? ssltest::tlcpFixture("ca.crt") : clientCaFile; const std::string signCert = ssltest::tlcpFixture("server_sign.crt"); const std::string signKey = ssltest::tlcpFixture("server_sign.key"); const std::string encCert = ssltest::tlcpFixture("server_enc.crt"); @@ -62,18 +93,13 @@ bool startTlcpServer(ssltest::OpenSslServerProcess& server, bool requireClientCe } std::vector args = { - "-enable_ntls", - "-ntls", - "-CAfile", caFile, - "-sign_cert", signCert, - "-sign_key", signKey, - "-enc_cert", encCert, - "-enc_key", encKey, - "-www", + "-enable_ntls", "-ntls", "-CAfile", caFile, "-sign_cert", signCert, "-sign_key", + signKey, "-enc_cert", encCert, "-enc_key", encKey, "-www", }; if (requireClientCert) { args.push_back("-Verify"); - args.push_back("1"); + args.push_back("2"); + args.push_back("-verify_return_error"); } return server.start(args) && server.running() && server.port() > 0; } @@ -93,7 +119,8 @@ TEST_CASE("TLCP one-way auth fails when server requires client certificate", "[r #if WITH_SSL ssltest::OpenSslServerProcess server; REQUIRE(startTlcpServer(server, true)); - REQUIRE_FALSE(ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); + REQUIRE_FALSE( + ssltest::tlsHandshakeWithSslConfig(tlcpTrustOnlyConfig(), "127.0.0.1", server.port())); server.stop(); #endif } @@ -111,3 +138,17 @@ TEST_CASE("TLCP mutual auth handshake with dual PKCS12 client store", "[rpc][ntl server.stop(); #endif } + +TEST_CASE("TLCP mutual auth sends the intermediate PEM certificate chain", "[rpc][ntls][e2e]") { +#if WITH_SSL + IntermediatePemCredentials files; + ssltest::OpenSslServerProcess server; + REQUIRE(startTlcpServer(server, true, ssltest::tlcpFixture("intermediate_root.crt"))); + + SslConfig config = tlcpTrustOnlyConfig(); + config.tlcpCertChainFile = files.certChain; + config.tlcpPrivateKeyFile = files.privateKeys; + REQUIRE(ssltest::tlsHandshakeWithSslConfig(config, "127.0.0.1", server.port())); + server.stop(); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp index f749d661b421..04584a80137d 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlcpMutualAuthTest.cpp @@ -19,7 +19,9 @@ #include +#include #include +#include #include "Common.h" #include "RpcSslUtils.h" @@ -32,6 +34,27 @@ bool fixtureExists(const std::string& path) { return in.good(); } +void concatenate(const std::string& output, std::initializer_list inputs) { + std::ofstream out(output, std::ios::binary | std::ios::trunc); + REQUIRE(out.good()); + for (const auto& input : inputs) { + std::ifstream in(input, std::ios::binary); + REQUIRE(in.good()); + out << in.rdbuf(); + out << '\n'; + } +} + +struct TemporaryPemBundles { + std::string certChain = "tongsuo-client-certs.pem"; + std::string privateKeys = "tongsuo-client-keys.pem"; + + ~TemporaryPemBundles() { + std::remove(certChain.c_str()); + std::remove(privateKeys.c_str()); + } +}; + } // namespace TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl][mutual]") { @@ -58,3 +81,30 @@ TEST_CASE("TLCP mutual auth creates client SSL_CTX from dual PKCS12", "[rpc][ssl SSL_CTX_free(ctx); #endif } + +TEST_CASE("TLCP mutual auth creates client SSL_CTX from PEM bundles", "[rpc][ssl][mutual]") { +#if WITH_SSL + TemporaryPemBundles files; + concatenate(files.certChain, + {ssltest::tlcpFixture("client_sign.crt"), ssltest::tlcpFixture("client_enc.crt"), + ssltest::tlcpFixture("ca.crt")}); + concatenate(files.privateKeys, + {ssltest::tlcpFixture("client_sign.key"), ssltest::tlcpFixture("client_enc.key")}); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLCP"; + config.trustStore = ssltest::tlcpFixture("ca.crt"); + config.tlcpCertChainFile = files.certChain; + config.tlcpPrivateKeyFile = files.privateKeys; + config.tlcpPrivateKeyPwd = ssltest::kStorePassword; + + SSL_CTX* ctx = RpcSslUtils::createClientSslContext(config); + REQUIRE(ctx != nullptr); + STACK_OF(X509)* certificateChain = nullptr; + SSL_CTX_get_extra_chain_certs(ctx, &certificateChain); + REQUIRE(certificateChain != nullptr); + REQUIRE(sk_X509_num(certificateChain) == 1); + SSL_CTX_free(ctx); +#endif +} diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp index 54b48e67667e..4502aec8379f 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslUtilsTest.cpp @@ -17,7 +17,6 @@ * under the License. */ - #include #include "Common.h" @@ -55,7 +54,7 @@ TEST_CASE("RpcSslUtils store validation rejects missing files", "[rpc][ssl]") { IoTDBException); } -#if WITH_SSL +#if WITH_SSL && defined(IOTDB_NTLS_PROVIDER_TONGSUO) TEST_CASE("RpcSslUtils createClientSslContext for TLS without trust store", "[rpc][ssl]") { SslConfig config; config.useSsl = true; diff --git a/iotdb-client/client-cpp/test/fixtures/gmssl/ca.crt b/iotdb-client/client-cpp/test/fixtures/gmssl/ca.crt new file mode 100644 index 000000000000..c0cf392cceac --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/gmssl/ca.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhzCCAS2gAwIBAgIMKH7jQLYO1PZdqFHcMAoGCCqBHM9VAYN1MB4xHDAaBgNV +BAMTE0lvVERCIEdtU1NMIFRlc3QgQ0EwHhcNMjYwOTA3MDYyMDU2WhcNMzYwOTA0 +MDYyMDU2WjAeMRwwGgYDVQQDExNJb1REQiBHbVNTTCBUZXN0IENBMFkwEwYHKoZI +zj0CAQYIKoEcz1UBgi0DQgAEc64x3K+eja7LVGkHdG6NgB7PDde/pZ9CsBeR2Vy6 +odKBxF+8E0bPhIF96MK/5OvMPK5/On8ErutTCpmKqT7W46NRME8wKQYDVR0OBCIE +IA9trTVKmcx2xJmwRczKPKcAkiDZKqlR5dFk5Tw7e9XbMA4GA1UdDwEB/wQEAwIB +BjASBgNVHRMBAf8ECDAGAQH/AgEBMAoGCCqBHM9VAYN1A0gAMEUCIQDV+AR+bVex +wGqi2P2ndiqldKEgMI8VEei5dEEUuIn7swIgEzbvHHoVTGBzZ8tOZm/iVRPcc4qE +NSV7TZhvJM5RTYM= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/gmssl/client.crt b/iotdb-client/client-cpp/test/fixtures/gmssl/client.crt new file mode 100644 index 000000000000..52515f0fdd5b --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/gmssl/client.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqDCCAU6gAwIBAgIMHKxjOdCmc6zYLMjeMAoGCCqBHM9VAYN1MB4xHDAaBgNV +BAMTE0lvVERCIEdtU1NMIFRlc3QgQ0EwHhcNMjYwOTA3MDYyMDU2WhcNMzYwOTA0 +MDYyMDU2WjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAASU68HqabwxAdYF3l/UJpS+5U9eAOxK0XCeDj+eQ7HFD2D5YzBjro4h5JAF +pLnpOJ7bL6OMaXfjnjGQh857vmupo38wfTArBgNVHSMEJDAigCAPba01SpnMdsSZ +sEXMyjynAJIg2SqpUeXRZOU8O3vV2zApBgNVHQ4EIgQg2oPYwVHqpCS+64gEo8jE +ATEtDXfgN8R7T0mNEEkM8PEwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG +AQUFBwMCMAoGCCqBHM9VAYN1A0gAMEUCIEL4Bdk83gz9he5bosK9dSBITb4uLBOy +TJv1xX2wIQFdAiEAiontpSPcEa/+pP04BlIkgkBHvp+iJ2U+saNYmCZnrcQ= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/gmssl/client.key b/iotdb-client/client-cpp/test/fixtures/gmssl/client.key new file mode 100644 index 000000000000..7efecb00b4f9 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/gmssl/client.key @@ -0,0 +1,8 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQ5CMX/QhnD5xS9Thl +e41MuwIDAQAAAgEQMAsGCSqBHM9VAYMRAjAcBggqgRzPVQFoAgQQvfnM72RRJNTE +HFqx4mBGcQSBoJiwMS7M2EcQAv32vi0pht9ggUrCmzkaLNB8w1Tq608Q94882GlU +yx0q7HL/RwLczHz/qsLKQcGzcKCg0+69v+WE7Wf99p6bhQ0+/OuAW82iu8zVTvx8 +0C7/37R56YyzQjGUjvbyZxPvMLmPAWm78OPE2OFDbNBE2P2SqLZgVOv9E2E2PM7x +gaix/uAStdKwUt6nMWf9peFyYCLRDY7VIww= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/gmssl/server-certs.pem b/iotdb-client/client-cpp/test/fixtures/gmssl/server-certs.pem new file mode 100644 index 000000000000..9796ce26b621 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/gmssl/server-certs.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIBzzCCAXWgAwIBAgINAJIKP/zXtMYmxWeLVTAKBggqgRzPVQGDdTAeMRwwGgYD +VQQDExNJb1REQiBHbVNTTCBUZXN0IENBMB4XDTI2MDkwNzA2MjA1NloXDTM2MDkw +NDA2MjA1NlowFDESMBAGA1UEAxMJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYIKoEc +z1UBgi0DQgAEPin+LRtg2n6WQ7PI+niMhhGNqRiCTZnHsBXnLdQewRJ5x9zsXQ50 +HnPAkXdJkV0vu55CJmSkIsefp9jND1xpCaOBoTCBnjArBgNVHSMEJDAigCAPba01 +SpnMdsSZsEXMyjynAJIg2SqpUeXRZOU8O3vV2zApBgNVHQ4EIgQgc+fYClafjgFJ +Qyq7pYFG+8BE2pqayLFSBwg7GeyZW/gwDgYDVR0PAQH/BAQDAgeAMB8GA1UdEQQY +MBaCCTEyNy4wLjAuMYIJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAoG +CCqBHM9VAYN1A0gAMEUCIQC9jAE3oSgLriSuBRkr+ZrFJcxkTMmUldMpFACDErXm +dAIgLumi8hoervwNT6W6jllje9MF/adCeZza19JKnEPzP/E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBzjCCAXSgAwIBAgIMDvk3T/Q22AFSlXI0MAoGCCqBHM9VAYN1MB4xHDAaBgNV +BAMTE0lvVERCIEdtU1NMIFRlc3QgQ0EwHhcNMjYwOTA3MDYyMDU2WhcNMzYwOTA0 +MDYyMDU2WjAUMRIwEAYDVQQDEwkxMjcuMC4wLjEwWTATBgcqhkjOPQIBBggqgRzP +VQGCLQNCAAQ+LhfEdMZUhIQLnObbSC0NN+anF0aL5hj1M+s6Pfg+Et7r0pm2wk57 +sD5NnZp8kLlHHFDSm71RxAHO8C7Fbf0Ro4GhMIGeMCsGA1UdIwQkMCKAIA9trTVK +mcx2xJmwRczKPKcAkiDZKqlR5dFk5Tw7e9XbMCkGA1UdDgQiBCDnpXOxsaMMX/T0 +9jXGj8vLnIeYLduZN80UYov7Hpkb9zAOBgNVHQ8BAf8EBAMCAzgwHwYDVR0RBBgw +FoIJMTI3LjAuMC4xgglsb2NhbGhvc3QwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYI +KoEcz1UBg3UDSAAwRQIgDlidEsxL1G0tz5hjuzmLecM6aVou9ZcMXtu/bYatX+YC +IQC28s0mFYlUmV0hNCC8GDJfjgDyHiJoX2lJ7taaU6toxg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBhzCCAS2gAwIBAgIMKH7jQLYO1PZdqFHcMAoGCCqBHM9VAYN1MB4xHDAaBgNV +BAMTE0lvVERCIEdtU1NMIFRlc3QgQ0EwHhcNMjYwOTA3MDYyMDU2WhcNMzYwOTA0 +MDYyMDU2WjAeMRwwGgYDVQQDExNJb1REQiBHbVNTTCBUZXN0IENBMFkwEwYHKoZI +zj0CAQYIKoEcz1UBgi0DQgAEc64x3K+eja7LVGkHdG6NgB7PDde/pZ9CsBeR2Vy6 +odKBxF+8E0bPhIF96MK/5OvMPK5/On8ErutTCpmKqT7W46NRME8wKQYDVR0OBCIE +IA9trTVKmcx2xJmwRczKPKcAkiDZKqlR5dFk5Tw7e9XbMA4GA1UdDwEB/wQEAwIB +BjASBgNVHRMBAf8ECDAGAQH/AgEBMAoGCCqBHM9VAYN1A0gAMEUCIQDV+AR+bVex +wGqi2P2ndiqldKEgMI8VEei5dEEUuIn7swIgEzbvHHoVTGBzZ8tOZm/iVRPcc4qE +NSV7TZhvJM5RTYM= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/gmssl/server-keys.pem b/iotdb-client/client-cpp/test/fixtures/gmssl/server-keys.pem new file mode 100644 index 000000000000..af019f76d426 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/gmssl/server-keys.pem @@ -0,0 +1,16 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQ+pKL+uznRIQB///N +SXHRIAIDAQAAAgEQMAsGCSqBHM9VAYMRAjAcBggqgRzPVQFoAgQQBcrcir58gyH/ +zVe/1FhdWASBoK82GtpTf0d0OwPI9jbZ/NDgv5GKxqBIz21AGrDrjG3QD+sFW8AV +zyL42Yltvdm9J0OVuVwHCVGVm+r0zASHtx1lA4EY1B9s2rLuXjAk34xVzh4cH2bJ +BVHDfzfauSFjMal0zl4E77qjS8yxv8uZLE7N0k+px2WbkpCS9tUg5yYT2ws+xt32 +U+YHKugK3g/n2Y1IpPNFGomH7DBDWMTxt5Y= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQrsn3/cFqcTiLF9S5 +X+WiWAIDAQAAAgEQMAsGCSqBHM9VAYMRAjAcBggqgRzPVQFoAgQQJuYnZjhNprza +/R6OFWj+FASBoAhcnfsL/2E6VTEHP3Ww7Gz6GlIIXcXYOkQC0F40zDWhEEfbvw5j +scef8GNFytpkuhWXhLzhZfyzqs0KU0pRMDVwIkGcbCF5i86zIYYkkAmeZ6KzsNYT +GGtU1y1sfb7l9rGtZ32xnb/eCCgHufuB6VQ+DG8apU0omkhhUIIHndWT8nITqIPD +gIoFEt8HTXy1JILfp7O1psNMo9RphDp+AZg= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_ca.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_ca.crt new file mode 100644 index 000000000000..cc062198a1ab --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_ca.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtDCCAVqgAwIBAgIUGcWM1awdQfYpDykkpQljaD7SJvAwCgYIKoEcz1UBg3Uw +JzElMCMGA1UEAwwcSW9UREIgVExDUCBJbnRlcm1lZGlhdGUgUm9vdDAeFw0yNjA5 +MDcwNjE3NTJaFw0zNjA5MDQwNjE3NTJaMCUxIzAhBgNVBAMMGklvVERCIFRMQ1Ag +SW50ZXJtZWRpYXRlIENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyklx/mPp +E0GD6D9oJIwc326XkGMaG+5O9KePwF0MBdkYmDayXCu5r1vv8k1gwXAE2YdXBQ9L +CuVqnN0wshmwJaNmMGQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFM1GZKmy204Fl9/yNmcUdTDjOjgrMB8GA1UdIwQYMBaAFBsz +Te+705g3nVf3nKqijkDIjYZWMAoGCCqBHM9VAYN1A0gAMEUCIEgJl6LSXSCQFX+5 +5UUk87baiup9s4m90y9odgCQ+fF5AiEA+gsn8Bp4fZvdrvC1sjgnwuhbsv/s0LFk +oG5j3jv7LXQ= +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.crt new file mode 100644 index 000000000000..a730c1cef66e --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBvjCCAWSgAwIBAgIUWiE+1u/yvx+ZiTydD5HQLOwjLYwwCgYIKoEcz1UBg3Uw +JTEjMCEGA1UEAwwaSW9UREIgVExDUCBJbnRlcm1lZGlhdGUgQ0EwHhcNMjYwOTA3 +MDYxNzUyWhcNMzYwOTA0MDYxNzUyWjAiMSAwHgYDVQQDDBdpbnRlcm1lZGlhdGUg +Y2xpZW50IGVuYzBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABH3Sj1uxAJF2xsvY +Md3K+jkromLK9lcQy9isW4cT+53idoNdpEgsd1yDslYReDA3HGV8RooeimI45wE0 +PeJ7LZSjdTBzMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgM4MBMGA1UdJQQM +MAoGCCsGAQUFBwMCMB0GA1UdDgQWBBTgU0BAwtD0fq7kg22GWpudha3SwjAfBgNV +HSMEGDAWgBTNRmSpsttOBZff8jZnFHUw4zo4KzAKBggqgRzPVQGDdQNIADBFAiEA +pxaJNVW9T2Boaa4uJjHt6c7NVA8ZKp6UhuOfAnJ2XQkCICrPpWQCfzT/j1I+ZWUI +xC9Zn+v2Vvkx/QzX3YFekgqT +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.key b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.key new file mode 100644 index 000000000000..c8d451439a63 --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_enc.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgejUmcf19CjRBRNRe +8GKO2DGlx5A4naZ3PQ0Yeo94MLuhRANCAAR90o9bsQCRdsbL2DHdyvo5K6JiyvZX +EMvYrFuHE/ud4naDXaRILHdcg7JWEXgwNxxlfEaKHopiOOcBND3iey2U +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.crt new file mode 100644 index 000000000000..5e59acf0ccec --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBvjCCAWWgAwIBAgIUIFFwN1rsGTOcYrJl4GmIAj9LHIEwCgYIKoEcz1UBg3Uw +JTEjMCEGA1UEAwwaSW9UREIgVExDUCBJbnRlcm1lZGlhdGUgQ0EwHhcNMjYwOTA3 +MDYxNzUyWhcNMzYwOTA0MDYxNzUyWjAjMSEwHwYDVQQDDBhpbnRlcm1lZGlhdGUg +Y2xpZW50IHNpZ24wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAARvB9v4Juui1DIO +XMdGhE6Hn6vjYSDmS+SfPJSNGMLlV46zIsRwnfCrbj3+BHUTuQ0UAqqoMbs6dSdw +vXOLoE3lo3UwczAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE +DDAKBggrBgEFBQcDAjAdBgNVHQ4EFgQUeE81HElIpt0iKxzBKYh740xiXTMwHwYD +VR0jBBgwFoAUzUZkqbLbTgWX3/I2ZxR1MOM6OCswCgYIKoEcz1UBg3UDRwAwRAIg +W1AXRQHe81XikQWvXBDfQ5iMJCsrY2J71f55LPE5xlACIBELIjF3peiNC1DgYN6K +mG9+GI/ejW9kHVJcCFhVGkJd +-----END CERTIFICATE----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.key b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.key new file mode 100644 index 000000000000..7b0b25f1d11d --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_client_sign.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgVds7KmKbH8/BFp5q +hcsONAkyiIL86b/kMzvEpTEONzGhRANCAARvB9v4Juui1DIOXMdGhE6Hn6vjYSDm +S+SfPJSNGMLlV46zIsRwnfCrbj3+BHUTuQ0UAqqoMbs6dSdwvXOLoE3l +-----END PRIVATE KEY----- diff --git a/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_root.crt b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_root.crt new file mode 100644 index 000000000000..d0f7b68f3f1a --- /dev/null +++ b/iotdb-client/client-cpp/test/fixtures/tlcp/intermediate_root.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtzCCAVygAwIBAgIUHY5kMnQgGgMfnGOyj4FLPWtYHrYwCgYIKoEcz1UBg3Uw +JzElMCMGA1UEAwwcSW9UREIgVExDUCBJbnRlcm1lZGlhdGUgUm9vdDAeFw0yNjA5 +MDcwNjE3NTJaFw0zNjA5MDQwNjE3NTJaMCcxJTAjBgNVBAMMHElvVERCIFRMQ1Ag +SW50ZXJtZWRpYXRlIFJvb3QwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAARmLxUo +/zSpCqeRou/n8sAYa4RJLA7vvFoh1tRLm/12NKVIP+aqow1wC1HrtteGKskLnNfn +Ea65DWj69MYz/fsAo2YwZDAdBgNVHQ4EFgQUGzNN77vTmDedV/ecqqKOQMiNhlYw +HwYDVR0jBBgwFoAUGzNN77vTmDedV/ecqqKOQMiNhlYwEgYDVR0TAQH/BAgwBgEB +/wIBATAOBgNVHQ8BAf8EBAMCAQYwCgYIKoEcz1UBg3UDSQAwRgIhAKTl76cpA4Vz +KQMYrU6PN/JUZuSR0bodJQ9YW4RQyMAbAiEAp6OAiZthuja07qycjtxdgbNKpUnR +339zlvTEGNzE98o= +-----END CERTIFICATE----- From 839e3b78c0c25c49b41a7259fb8791cf3d8dde07 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Mon, 7 Sep 2026 15:09:03 +0800 Subject: [PATCH 16/24] fix(client-cpp): stop TLS test server after phase --- .../test/scripts/run_cpp_it_phases.py | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py index 3aa7d9d3ce0c..020f98061fcd 100644 --- a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py +++ b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py @@ -62,13 +62,23 @@ def start_iotdb(dist_root: Path, start_script: Path, wait_s: int) -> None: def main() -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("build_dir", help="CMake build directory containing CTestTestfile.cmake") + parser.add_argument( + "build_dir", help="CMake build directory containing CTestTestfile.cmake" + ) parser.add_argument("dist_root", help="IoTDB distribution root") parser.add_argument("fixtures_root", help="C++ test fixtures root") - parser.add_argument("scripts_root", help="Directory containing configure_iotdb_ssl_it.py") - parser.add_argument("start_script", help="Relative path to start-standalone script under dist sbin/") - parser.add_argument("--config", default="Release", help="CTest build configuration (MSVC)") - parser.add_argument("--wait-seconds", type=int, default=45, help="Seconds to wait after IoTDB start") + parser.add_argument( + "scripts_root", help="Directory containing configure_iotdb_ssl_it.py" + ) + parser.add_argument( + "start_script", help="Relative path to start-standalone script under dist sbin/" + ) + parser.add_argument( + "--config", default="Release", help="CTest build configuration (MSVC)" + ) + parser.add_argument( + "--wait-seconds", type=int, default=45, help="Seconds to wait after IoTDB start" + ) args = parser.parse_args() build_dir = Path(args.build_dir).resolve() @@ -91,8 +101,12 @@ def main() -> int: [sys.executable, str(configure), str(dist_root), str(fixtures_root), "enable"], cwd=scripts_root, ) - start_iotdb(dist_root, start_script, args.wait_seconds) - run(ctest_base + ["-L", "ssl"], build_dir) + try: + start_iotdb(dist_root, start_script, args.wait_seconds) + run(ctest_base + ["-L", "ssl"], build_dir) + finally: + stop_iotdb(dist_root) + print("=== Phase 2b: NTLS (no IoTDB; openssl s_server) ===") run(ctest_base + ["-L", "ntls"], build_dir) From 2a31ad02a6d26dc775313e2833371c4199c1d750 Mon Sep 17 00:00:00 2001 From: hongzhigao <761417898@qq.com> Date: Tue, 8 Sep 2026 09:22:53 +0800 Subject: [PATCH 17/24] fix(client-cpp): address SSL review issues --- .../package-client-cpp-manylinux228.sh | 2 +- LICENSE-binary | 2 +- iotdb-client/client-cpp/CMakeLists.txt | 9 ++- iotdb-client/client-cpp/README.md | 13 ++-- iotdb-client/client-cpp/README_zh.md | 4 +- .../client-cpp/cmake/FetchOpenSSL.cmake | 27 +++++++- .../client-cpp/examples/CMakeLists.txt | 40 +++++++----- iotdb-client/client-cpp/pom.xml | 4 +- .../third_party/DEPENDENCIES.md | 2 +- iotdb-client/client-cpp/src/include/Session.h | 3 +- .../client-cpp/src/include/SslConfig.h | 43 ++++++++++++ .../client-cpp/src/include/TableSession.h | 4 +- .../client-cpp/src/rpc/RpcSslUtils.cpp | 16 ++--- iotdb-client/client-cpp/src/rpc/RpcSslUtils.h | 20 +----- .../test/cpp/RpcSslIotdbE2eTest.cpp | 31 +++++---- .../test/cpp/RpcSslTlsMutualAuthTest.cpp | 65 ++++++++++++++++--- .../client-cpp/test/cpp/SslTestFixtures.cpp | 17 +++-- .../client-cpp/test/cpp/sessionIT.cpp | 12 +--- .../client-cpp/test/tools/GenTlcpDualP12.cpp | 4 +- iotdb-client/client-cpp/third-party/README.md | 2 +- 20 files changed, 212 insertions(+), 108 deletions(-) create mode 100644 iotdb-client/client-cpp/src/include/SslConfig.h diff --git a/.github/scripts/package-client-cpp-manylinux228.sh b/.github/scripts/package-client-cpp-manylinux228.sh index a90aaaa75e38..be68fb1fc0a3 100644 --- a/.github/scripts/package-client-cpp-manylinux228.sh +++ b/.github/scripts/package-client-cpp-manylinux228.sh @@ -73,7 +73,7 @@ java -version # manylinux_2_28 is AlmaLinux 8, whose system OpenSSL is 1.1.1 (EOL and not # Apache-2.0 - must not be bundled/redistributed in an ASF convenience binary). -# Tongsuo 8.4-stable is always built from source (WITH_SSL=ON), which keeps the +# A pinned Tongsuo 8.4-stable commit is always built from source (WITH_SSL=ON), which keeps the # glibc 2.28 baseline. Tongsuo's Configure needs perl plus a # few modules (IPC::Cmd, Data::Dumper) that are not on the minimal image - # install them even when perl itself is already present. diff --git a/LICENSE-binary b/LICENSE-binary index a92f3b13fe23..0430c04658b6 100644 --- a/LICENSE-binary +++ b/LICENSE-binary @@ -262,7 +262,7 @@ org.osgi:org.osgi.core:7.0.0 org.osgi:osgi.cmpn:7.0.0 org.ops4j.pax.jdbc:pax-jdbc-common:1.5.6 org.xerial.snappy:snappy-java:1.1.10.5 -Tongsuo:Tongsuo:8.4-stable (default C++ NTLS provider) +Tongsuo:Tongsuo:8.4-stable commit 0aed892c (default C++ NTLS provider) io.airlift.airline:0.9 diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index 9364d24aaad5..bfc441ffc4d0 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -106,8 +106,10 @@ string(TOUPPER "${IOTDB_NTLS_PROVIDER}" IOTDB_NTLS_PROVIDER) if(NOT IOTDB_NTLS_PROVIDER MATCHES "^(TONGSUO|GMSSL)$") message(FATAL_ERROR "IOTDB_NTLS_PROVIDER must be TONGSUO or GMSSL") endif() -set(TONGSUO_GIT_REF "8.4-stable" - CACHE STRING "Tongsuo git ref used when building SSL/TLS from source") +set(TONGSUO_GIT_REF "0aed892c5f48c9a52d1f5667667ae45156b9cdf4" + CACHE STRING "Pinned Tongsuo git commit used when building SSL/TLS from source") +set(TONGSUO_SHA256 "4bb302df8ff73a89b3483873d10e7b6a4eeb041310b8eabd2be309004a88f8b6" + CACHE STRING "SHA-256 of the pinned Tongsuo source archive") set(IOTDB_GMSSL_ROOT_DIR "" CACHE PATH "Preinstalled GmSSL 3.2 root (required for the GMSSL provider)") @@ -234,6 +236,7 @@ include(GNUInstallDirs) set(IOTDB_PUBLIC_HEADERS Export.h SessionConfig.h + SslConfig.h Session.h Common.h Optional.h @@ -314,6 +317,8 @@ file(WRITE "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt" "cmake.build.type=${CMAKE_BUILD_TYPE}\n" "with.ssl=${WITH_SSL}\n" "ntls.provider=${IOTDB_NTLS_PROVIDER}\n" + "tongsuo.git.ref=${TONGSUO_GIT_REF}\n" + "tongsuo.sha256=${TONGSUO_SHA256}\n" "iotdb.offline=${IOTDB_OFFLINE}\n" "iotdb.use.cxx11.abi=${IOTDB_USE_CXX11_ABI}\n" "iotdb.extra.cxx.flags=${IOTDB_EXTRA_CXX_FLAGS}\n") diff --git a/iotdb-client/client-cpp/README.md b/iotdb-client/client-cpp/README.md index ed6d1b057486..c279d2f7f814 100644 --- a/iotdb-client/client-cpp/README.md +++ b/iotdb-client/client-cpp/README.md @@ -388,7 +388,8 @@ etc. directly. | `BOOST_VERSION` | `1.60.0` (`1.84.0` on macOS) | Boost version that CMake will look for / download. | | `THRIFT_VERSION` | `0.24.0` | Apache Thrift version to build from source. | | `IOTDB_NTLS_PROVIDER` | `TONGSUO` | NTLS provider: `TONGSUO` or `GMSSL`. | -| `TONGSUO_GIT_REF` | `8.4-stable` | Tongsuo git ref built from source when `WITH_SSL=ON`. | +| `TONGSUO_GIT_REF` | commit `0aed892c` | Pinned Tongsuo 8.4-stable commit built from source when `WITH_SSL=ON`. | +| `TONGSUO_SHA256` | pinned archive hash | SHA-256 used to verify the Tongsuo source archive and offline cache. | | `IOTDB_GMSSL_ROOT_DIR` | (unset) | Preinstalled GmSSL 3 root required by the `GMSSL` provider. | | `BOOST_ROOT` | (unset) | Existing Boost install to reuse, equivalent to `-Dboost.include.dir=...` from the legacy build. | | `CMAKE_INSTALL_PREFIX`| `/install` | Install location. | @@ -431,8 +432,8 @@ cmake --build build --config Release --target install | Platform | Required files | |------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| - | `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4-stable.tar.gz` | - | `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_84_0.tar.gz`, `tongsuo-8.4-stable.tar.gz` (Apple ships m4/flex/bison) | + | `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-0aed892c5f48c9a52d1f5667667ae45156b9cdf4.tar.gz` | + | `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_84_0.tar.gz`, `tongsuo-0aed892c5f48c9a52d1f5667667ae45156b9cdf4.tar.gz` (Apple ships m4/flex/bison) | | `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz` (Boost headers only - no `b2` build required for `iotdb_session`) | Reference URLs (the configure step uses the same): @@ -441,7 +442,7 @@ cmake --build build --config Release --target install - GNU m4 1.4.19: - GNU flex 2.6.4: - GNU bison 3.8: - - Tongsuo 8.4-stable: + - Tongsuo 8.4-stable commit `0aed892c`: 2. Run the build with offline mode enabled: @@ -497,7 +498,7 @@ Prerequisites: and rename `win_flex.exe`→`flex.exe`, `win_bison.exe`→`bison.exe` on `PATH`. 3. **Perl** (for building Tongsuo when `WITH_SSL=ON`). -4. **Tongsuo / SSL** *(`WITH_SSL=ON` is the default)*: Tongsuo 8.4-stable is +4. **Tongsuo / SSL** *(`WITH_SSL=ON` is the default)*: a pinned Tongsuo 8.4-stable commit is always built from source (requires Perl and `nmake` from the VS Developer Command Prompt). Pass `-DWITH_SSL=OFF` to build without SSL. @@ -514,7 +515,7 @@ the GNU autotools tarballs assume a POSIX shell environment. `iotdb_session` builds with SSL/TLS by default. Supported NTLS providers: -- `TONGSUO` (default): Tongsuo 8.4-stable, built from source; TLS/TLCP and +- `TONGSUO` (default): pinned Tongsuo 8.4-stable commit `0aed892c`, built from source; TLS/TLCP and PKCS12 or PEM credentials. - `GMSSL`: preinstalled GmSSL 3.2 using its native TLCP API; TLCP with PEM credentials. OCL is not used because it does not implement diff --git a/iotdb-client/client-cpp/README_zh.md b/iotdb-client/client-cpp/README_zh.md index fd35578c6435..d1da12eb38b1 100644 --- a/iotdb-client/client-cpp/README_zh.md +++ b/iotdb-client/client-cpp/README_zh.md @@ -238,6 +238,8 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 |------------|------------| | `WITH_SSL` | `with.ssl`(默认 `ON`,关闭用 `-Dwith.ssl=OFF`) | | `IOTDB_NTLS_PROVIDER` | `ntls.provider`(`TONGSUO` 或 `GMSSL`) | +| `TONGSUO_GIT_REF` | `tongsuo.git.ref`(默认固定到 commit `0aed892c`) | +| `TONGSUO_SHA256` | `tongsuo.sha256`(Tongsuo 源码归档校验值) | | `IOTDB_GMSSL_ROOT_DIR` | `gmssl.root.dir` | | `IOTDB_OFFLINE` | `iotdb.offline` | | `BUILD_TESTING` | `build.tests` | @@ -247,7 +249,7 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成 SSL 默认开启(`WITH_SSL=ON`)。支持的 NTLS Provider: -- `TONGSUO`(默认):源码构建 Tongsuo 8.4-stable,支持 TLS/TLCP 及 +- `TONGSUO`(默认):源码构建固定到 commit `0aed892c` 的 Tongsuo 8.4-stable,支持 TLS/TLCP 及 PKCS12、PEM 凭据。 - `GMSSL`:使用预安装的 GmSSL 3.2 原生 TLCP API,支持 TLCP 及 PEM 凭据。 OCL 未实现 Thrift 所需的完整 OpenSSL API,因此不使用 OCL。 diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index 17be39c9ae31..cbb5c55da831 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -28,7 +28,10 @@ # --- Default provider: build Tongsuo ${TONGSUO_GIT_REF} from source --- if(IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") -if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]{7,40}$") +string(LENGTH "${TONGSUO_GIT_REF}" _tongsuo_git_ref_length) +if(TONGSUO_GIT_REF MATCHES "^[0-9a-fA-F]+$" + AND _tongsuo_git_ref_length GREATER_EQUAL 7 + AND _tongsuo_git_ref_length LESS_EQUAL 40) set(_tongsuo_extracted_dir "Tongsuo-${TONGSUO_GIT_REF}") set(_tongsuo_url "https://github.com/Tongsuo-Project/Tongsuo/archive/${TONGSUO_GIT_REF}.tar.gz") else() @@ -40,6 +43,25 @@ endif() set(_tongsuo_tarname "tongsuo-${TONGSUO_GIT_REF}.tar.gz") set(_tongsuo_tarball "${IOTDB_OS_DEPS_DIR}/${_tongsuo_tarname}") +string(LENGTH "${TONGSUO_SHA256}" _tongsuo_sha256_length) +if(NOT TONGSUO_SHA256 MATCHES "^[0-9a-fA-F]+$" OR NOT _tongsuo_sha256_length EQUAL 64) + message(FATAL_ERROR + "[Tongsuo] TONGSUO_SHA256 must be the 64-character SHA-256 of ${_tongsuo_tarname}") +endif() + +if(EXISTS "${_tongsuo_tarball}") + file(SHA256 "${_tongsuo_tarball}" _tongsuo_cached_sha256) + if(NOT "${_tongsuo_cached_sha256}" STREQUAL "${TONGSUO_SHA256}") + if(IOTDB_OFFLINE) + message(FATAL_ERROR + "[Tongsuo] cached ${_tongsuo_tarname} has SHA-256 ${_tongsuo_cached_sha256}; " + "expected ${TONGSUO_SHA256}") + endif() + message(STATUS "[Tongsuo] removing cached archive with mismatched SHA-256") + file(REMOVE "${_tongsuo_tarball}") + endif() +endif() + if(NOT EXISTS "${_tongsuo_tarball}") if(IOTDB_OFFLINE) message(FATAL_ERROR @@ -49,6 +71,7 @@ if(NOT EXISTS "${_tongsuo_tarball}") file(DOWNLOAD "${_tongsuo_url}" "${_tongsuo_tarball}" SHOW_PROGRESS TLS_VERIFY ON TIMEOUT 600 + EXPECTED_HASH "SHA256=${TONGSUO_SHA256}" STATUS _st) list(GET _st 0 _code) if(NOT _code EQUAL 0) @@ -61,7 +84,7 @@ endif() set(_tongsuo_root "${CMAKE_BINARY_DIR}/_deps/tongsuo") set(_tongsuo_src "${_tongsuo_root}/src/${_tongsuo_extracted_dir}") set(_tongsuo_inst "${_tongsuo_root}/install") -set(_tongsuo_stamp "${_tongsuo_root}/.built-${TONGSUO_GIT_REF}") +set(_tongsuo_stamp "${_tongsuo_root}/.built-${TONGSUO_GIT_REF}-${TONGSUO_SHA256}") if(NOT EXISTS "${_tongsuo_stamp}") file(REMOVE_RECURSE "${_tongsuo_root}/src") diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index cf21c83b79a1..4d7aaa86cf06 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -159,18 +159,10 @@ set(_it_ntls_examples "") if(_iotdb_examples_in_tree) ADD_EXECUTABLE(cpp_tree_example cpp_tree_example.cpp) ADD_EXECUTABLE(cpp_table_example cpp_table_example.cpp) - ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) - ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) - ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) - ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) list(APPEND _example_targets cpp_tree_example - cpp_table_example - cpp_tls_example - cpp_ntls_example - tls_tree_example - c_ntls_example) + cpp_table_example) set(_it_plain_examples cpp_tree_example @@ -178,13 +170,28 @@ if(_iotdb_examples_in_tree) tree_example table_example) - set(_it_ssl_examples - cpp_tls_example - tls_tree_example) + # These examples exercise the OpenSSL-compatible Tongsuo API and command-line + # server. GmSSL has a separate native API and is covered by rpcNtlsUtilsTest. + if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") + ADD_EXECUTABLE(cpp_tls_example cpp_tls_example.cpp ExampleTlsConfig.cpp) + ADD_EXECUTABLE(cpp_ntls_example cpp_ntls_example.cpp ExampleNtlsHandshake.cpp) + ADD_EXECUTABLE(tls_tree_example tls_tree_example.c ExampleTlsConfig.cpp) + ADD_EXECUTABLE(c_ntls_example c_ntls_example.c ExampleNtlsHandshake.cpp) + + list(APPEND _example_targets + cpp_tls_example + cpp_ntls_example + tls_tree_example + c_ntls_example) - set(_it_ntls_examples - cpp_ntls_example - c_ntls_example) + set(_it_ssl_examples + cpp_tls_example + tls_tree_example) + + set(_it_ntls_examples + cpp_ntls_example + c_ntls_example) + endif() endif() foreach(_t IN LISTS _example_targets) @@ -263,9 +270,6 @@ if(_iotdb_examples_in_tree AND WITH_SSL AND IOTDB_EXAMPLES_REGISTER_TESTS) if(BOOST_INCLUDE_DIR) target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") endif() - if(BOOST_INCLUDE_DIR) - target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") - endif() file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir_cmake) string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir_cmake}") if(WIN32) diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index 20f4da09ecf1..c6d7eddd3a96 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -52,7 +52,8 @@ ON 0.24.0 TONGSUO - 8.4-stable + 0aed892c5f48c9a52d1f5667667ae45156b9cdf4 + 4bb302df8ff73a89b3483873d10e7b6a4eeb041310b8eabd2be309004a88f8b6 @@ -120,6 +121,7 @@ + diff --git a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md index 44c903d3a021..bc989cca6d98 100644 --- a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md +++ b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md @@ -33,7 +33,7 @@ the [`NOTICE`](NOTICE) file in this directory; non-Apache license texts are unde | --- | --- | --- | --- | | Apache Thrift | 0.24.0 | statically linked | Apache License 2.0 | | Boost | 1.60.0 on Linux/Windows, 1.84.0 on macOS by default | statically linked (header-only) | Boost Software License 1.0 | -| Tongsuo | 8.4-stable (default NTLS provider) | bundled shared libs in `lib/` | Apache License 2.0 | +| Tongsuo | 8.4-stable commit `0aed892c` (default NTLS provider) | bundled shared libs in `lib/` | Apache License 2.0 | | GmSSL | 3.2.x (optional NTLS provider) | bundled shared library in `lib/` | Apache License 2.0 | ## Build-time only (not redistributed) diff --git a/iotdb-client/client-cpp/src/include/Session.h b/iotdb-client/client-cpp/src/include/Session.h index 02294c63c8c6..dd615420efb1 100644 --- a/iotdb-client/client-cpp/src/include/Session.h +++ b/iotdb-client/client-cpp/src/include/Session.h @@ -19,8 +19,6 @@ #ifndef IOTDB_SESSION_H #define IOTDB_SESSION_H -struct SslConfig; - #include #include #include @@ -40,6 +38,7 @@ struct SslConfig; #include "Date.h" #include "DeviceID.h" #include "SessionDataSet.h" +#include "SslConfig.h" //== For compatible with Windows OS == #ifndef LONG_LONG_MIN diff --git a/iotdb-client/client-cpp/src/include/SslConfig.h b/iotdb-client/client-cpp/src/include/SslConfig.h new file mode 100644 index 000000000000..b32bb96dde20 --- /dev/null +++ b/iotdb-client/client-cpp/src/include/SslConfig.h @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef IOTDB_SSL_CONFIG_H +#define IOTDB_SSL_CONFIG_H + +#include + +struct SslConfig { + bool useSsl = false; + std::string sslProtocol = "TLS"; + std::string trustStore; + std::string trustStorePwd; + std::string keyStore; + std::string keyStorePwd; + /** TLCP PEM client certificate chain; provider-specific ordering is documented in README. */ + std::string tlcpCertChainFile; + /** TLCP PEM client private key bundle; provider-specific contents are documented in README. */ + std::string tlcpPrivateKeyFile; + std::string tlcpPrivateKeyPwd; + /** Legacy PEM trust certificate path; used when trustStore is empty. */ + std::string trustCertFilePath; + + std::string effectiveTrustStore() const; +}; + +#endif // IOTDB_SSL_CONFIG_H diff --git a/iotdb-client/client-cpp/src/include/TableSession.h b/iotdb-client/client-cpp/src/include/TableSession.h index a57288339b6f..2944b2355e83 100644 --- a/iotdb-client/client-cpp/src/include/TableSession.h +++ b/iotdb-client/client-cpp/src/include/TableSession.h @@ -24,8 +24,6 @@ #include "Session.h" -struct SslConfig; - class TableSession { private: std::shared_ptr session_; @@ -46,4 +44,4 @@ class TableSession { void setSslConfig(const SslConfig& sslConfig); }; -#endif // IOTDB_TABLESESSION_H \ No newline at end of file +#endif // IOTDB_TABLESESSION_H diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp index ea97e49a3be6..e5a8d33bbdf6 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp @@ -580,23 +580,24 @@ void applyTlsProtocolVersion(SSL_CTX* ctx, const std::string& protocol) { SSL_CTX* createTlsClientContext(const SslConfig& config) { const std::string protocol = RpcSslUtils::resolveProtocol(config.sslProtocol); - SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); + std::unique_ptr ctx(SSL_CTX_new(TLS_client_method()), + SSL_CTX_free); if (ctx == nullptr) { throwSslError("Failed to create TLS client context"); } - applyTlsProtocolVersion(ctx, protocol); + applyTlsProtocolVersion(ctx.get(), protocol); const std::string trustStore = config.effectiveTrustStore(); if (hasText(trustStore)) { - loadTrustStore(ctx, trustStore, config.trustStorePwd); - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + loadTrustStore(ctx.get(), trustStore, config.trustStorePwd); + SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr); } else { - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, nullptr); } if (hasText(config.keyStore)) { - loadTlsKeyStore(ctx, config.keyStore, config.keyStorePwd); + loadTlsKeyStore(ctx.get(), config.keyStore, config.keyStorePwd); } - return ctx; + return ctx.release(); } SSL_CTX* createTlcpClientContext(const SslConfig& config) { @@ -824,7 +825,6 @@ RpcSslUtils::createSslSocketFactory(const SslConfig& config) { SSL_CTX* ctx = createClientSslContext(*sslConfig); return std::make_shared(ctx); }); - factory->authenticate(false); return factory; } #elif defined(IOTDB_NTLS_PROVIDER_GMSSL) diff --git a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h index 8a3d9d345bc1..e6e1b20884e2 100644 --- a/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h +++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h @@ -23,6 +23,8 @@ #include #include +#include "SslConfig.h" + #if WITH_SSL #if defined(IOTDB_NTLS_PROVIDER_TONGSUO) #include @@ -32,24 +34,6 @@ #endif #endif -struct SslConfig { - bool useSsl = false; - std::string sslProtocol = "TLS"; - std::string trustStore; - std::string trustStorePwd; - std::string keyStore; - std::string keyStorePwd; - /** TLCP PEM client certificate chain; provider-specific ordering is documented in README. */ - std::string tlcpCertChainFile; - /** TLCP PEM client private key bundle; provider-specific contents are documented in README. */ - std::string tlcpPrivateKeyFile; - std::string tlcpPrivateKeyPwd; - /** Legacy PEM trust certificate path; used when trustStore is empty. */ - std::string trustCertFilePath; - - std::string effectiveTrustStore() const; -}; - class RpcSslUtils { public: static constexpr const char* DEFAULT_PROTOCOL = "TLS"; diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp index ef77b53034b6..a39e26325d42 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp @@ -48,7 +48,8 @@ TEST_CASE("TLS tree Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb][ } session->setStorageGroup(database); - session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, CompressionType::UNCOMPRESSED); + session->createTimeseries(timeseries, TSDataType::INT32, TSEncoding::PLAIN, + CompressionType::UNCOMPRESSED); session->insertRecord(database + ".d1", 1, {"s1"}, {"1"}); std::unique_ptr dataSet( @@ -76,7 +77,8 @@ TEST_CASE("TLS table Session connects to IoTDB and runs SQL", "[rpc][ssl][iotdb] session->executeNonQueryStatement("USE cpp_ssl_it_table"); session->executeNonQueryStatement( "CREATE TABLE IF NOT EXISTS ssl_it_table (tag1 STRING TAG, value INT32 FIELD)"); - session->executeNonQueryStatement("INSERT INTO ssl_it_table(time, tag1, value) VALUES (1, 't1', 42)"); + session->executeNonQueryStatement( + "INSERT INTO ssl_it_table(time, tag1, value) VALUES (1, 't1', 42)"); std::unique_ptr dataSet( session->executeQueryStatement("SELECT time, value FROM ssl_it_table WHERE tag1 = 't1'")); @@ -112,11 +114,12 @@ TEST_CASE("TLS C tree Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { TS_COMPRESSION_UNCOMPRESSED) == TS_OK); const char* measurements[] = {"s1"}; const char* values[] = {"1"}; - REQUIRE(ts_session_insert_record_str(session, "root.cpp_ssl_it_c.d1", 1, 1, measurements, values) == - TS_OK); + REQUIRE(ts_session_insert_record_str(session, "root.cpp_ssl_it_c.d1", 1, 1, measurements, + values) == TS_OK); CSessionDataSet* dataSet = nullptr; - REQUIRE(ts_session_execute_query(session, "SELECT s1 FROM root.cpp_ssl_it_c.d1", &dataSet) == TS_OK); + REQUIRE(ts_session_execute_query(session, "SELECT s1 FROM root.cpp_ssl_it_c.d1", &dataSet) == + TS_OK); REQUIRE(dataSet != nullptr); REQUIRE(ts_dataset_has_next(dataSet)); CRowRecord* record = ts_dataset_next(dataSet); @@ -140,18 +143,21 @@ TEST_CASE("TLS C table Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { it_ssl_configure_table_session(session); REQUIRE(ts_table_session_open(session) == TS_OK); - REQUIRE(ts_table_session_execute_non_query(session, "CREATE DATABASE IF NOT EXISTS cpp_ssl_it_c_table") == - TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, "CREATE DATABASE IF NOT EXISTS cpp_ssl_it_c_table") == TS_OK); REQUIRE(ts_table_session_execute_non_query(session, "USE cpp_ssl_it_c_table") == TS_OK); REQUIRE(ts_table_session_execute_non_query( session, - "CREATE TABLE IF NOT EXISTS ssl_it_c_table (tag1 STRING TAG, value INT32 FIELD)") == TS_OK); + "CREATE TABLE IF NOT EXISTS ssl_it_c_table (tag1 STRING TAG, value INT32 FIELD)") == + TS_OK); REQUIRE(ts_table_session_execute_non_query( - session, "INSERT INTO ssl_it_c_table(time, tag1, value) VALUES (1, 't1', 42)") == TS_OK); + session, "INSERT INTO ssl_it_c_table(time, tag1, value) VALUES (1, 't1', 42)") == + TS_OK); CSessionDataSet* dataSet = nullptr; - REQUIRE(ts_table_session_execute_query( - session, "SELECT time, value FROM ssl_it_c_table WHERE tag1 = 't1'", &dataSet) == TS_OK); + REQUIRE(ts_table_session_execute_query(session, + "SELECT time, value FROM ssl_it_c_table WHERE tag1 = 't1'", + &dataSet) == TS_OK); REQUIRE(dataSet != nullptr); REQUIRE(ts_dataset_has_next(dataSet)); CRowRecord* record = ts_dataset_next(dataSet); @@ -162,7 +168,8 @@ TEST_CASE("TLS C table Session connects to IoTDB", "[rpc][ssl][iotdb][e2e]") { ts_row_record_destroy(record); ts_dataset_destroy(dataSet); - REQUIRE(ts_table_session_execute_non_query(session, "DROP DATABASE IF EXISTS cpp_ssl_it_c_table") == TS_OK); + REQUIRE(ts_table_session_execute_non_query( + session, "DROP DATABASE IF EXISTS cpp_ssl_it_c_table") == TS_OK); REQUIRE(ts_table_session_close(session) == TS_OK); ts_table_session_destroy(session); } diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp index 309327f0272c..092226d2e37f 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslTlsMutualAuthTest.cpp @@ -34,7 +34,8 @@ bool fixtureExists(const std::string& path) { } // namespace -TEST_CASE("TLS mutual auth creates client SSL_CTX with trust and key stores", "[rpc][ssl][mutual]") { +TEST_CASE("TLS mutual auth creates client SSL_CTX with trust and key stores", + "[rpc][ssl][mutual]") { #if WITH_SSL const std::string trustStore = ssltest::tlsFixture("tls-trust.p12"); const std::string keyStore = ssltest::tlsFixture("tls-client.p12"); @@ -71,10 +72,14 @@ TEST_CASE("TLS mutual auth handshake with openssl s_server", "[rpc][ssl][mutual] ssltest::OpenSslServerProcess server; const bool started = server.start({ "-tls1_2", - "-Verify", "1", - "-CAfile", caFile, - "-cert", serverCert, - "-key", serverKey, + "-Verify", + "1", + "-CAfile", + caFile, + "-cert", + serverCert, + "-key", + serverKey, "-www", }); REQUIRE(started); @@ -94,7 +99,43 @@ TEST_CASE("TLS mutual auth handshake with openssl s_server", "[rpc][ssl][mutual] #endif } -TEST_CASE("TLS one-way auth fails when server requires client certificate", "[rpc][ssl][mutual][e2e]") { +TEST_CASE("TLS Thrift socket rejects a server outside the configured trust store", + "[rpc][ssl][verify][e2e]") { +#if WITH_SSL + const std::string serverCert = ssltest::tlsFixture("server.crt"); + const std::string serverKey = ssltest::tlsFixture("server.key"); + REQUIRE(fixtureExists(serverCert)); + REQUIRE(fixtureExists(serverKey)); + + ssltest::OpenSslServerProcess server; + REQUIRE(server.start({ + "-tls1_2", + "-cert", + serverCert, + "-key", + serverKey, + "-www", + })); + + SslConfig config; + config.useSsl = true; + config.sslProtocol = "TLS"; + config.trustStore = ssltest::tlcpFixture("tlcp-trust.p12"); + config.trustStorePwd = ssltest::kStorePassword; + + auto factory = RpcSslUtils::createSslSocketFactory(config); + auto socket = factory->createSocket("127.0.0.1", server.port()); + socket->setConnTimeout(2000); + REQUIRE_NOTHROW(socket->open()); + const uint8_t requestByte = 0; + REQUIRE_THROWS(socket->write(&requestByte, 1)); + socket->close(); + server.stop(); +#endif +} + +TEST_CASE("TLS one-way auth fails when server requires client certificate", + "[rpc][ssl][mutual][e2e]") { #if WITH_SSL const std::string caFile = ssltest::tlsFixture("ca.crt"); const std::string serverCert = ssltest::tlsFixture("server.crt"); @@ -104,10 +145,14 @@ TEST_CASE("TLS one-way auth fails when server requires client certificate", "[rp ssltest::OpenSslServerProcess server; const bool started = server.start({ "-tls1_2", - "-Verify", "1", - "-CAfile", caFile, - "-cert", serverCert, - "-key", serverKey, + "-Verify", + "1", + "-CAfile", + caFile, + "-cert", + serverCert, + "-key", + serverKey, "-www", }); REQUIRE(started); diff --git a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp index 05659823ef16..2500e5e8ebd5 100644 --- a/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp +++ b/iotdb-client/client-cpp/test/cpp/SslTestFixtures.cpp @@ -157,7 +157,7 @@ void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { } } -void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG) * bags, X509* cert, EVP_PKEY* key, const char* friendlyName, const std::string& password) { PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); @@ -224,7 +224,7 @@ void forEachPkcs12Bag(PKCS12* p12, const std::string& password, sk_PKCS7_pop_free(safes, PKCS7_free); } -void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, +void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG) * target, PKCS12* source, const std::string& password) { forEachPkcs12Bag(source, password, [&](PKCS12_SAFEBAG* bag) { const int bagType = PKCS12_SAFEBAG_get_nid(bag); @@ -244,8 +244,8 @@ void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, } else if (bagType == NID_pkcs8ShroudedKeyBag || bagType == NID_keyBag) { EVP_PKEY* key = nullptr; if (bagType == NID_pkcs8ShroudedKeyBag) { - PKCS8_PRIV_KEY_INFO* p8 = PKCS12_decrypt_skey(bag, password.c_str(), - static_cast(password.size())); + PKCS8_PRIV_KEY_INFO* p8 = + PKCS12_decrypt_skey(bag, password.c_str(), static_cast(password.size())); if (p8 != nullptr) { key = EVP_PKCS82PKEY(p8); PKCS8_PRIV_KEY_INFO_free(p8); @@ -261,8 +261,7 @@ void appendPkcs12Bags(STACK_OF(PKCS12_SAFEBAG)* target, PKCS12* source, EVP_PKEY_free(key); if (p8 != nullptr) { PKCS12_SAFEBAG* newBag = PKCS12_SAFEBAG_create_pkcs8_encrypt( - NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, - p8); + NID_pbes2, password.c_str(), static_cast(password.size()), nullptr, 0, 2048, p8); PKCS8_PRIV_KEY_INFO_free(p8); if (newBag != nullptr) { if (friendlyName != nullptr) { @@ -484,7 +483,7 @@ int findFreeTcpPort() { #endif return 0; } - sockaddr_in addr {}; + sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = 0; @@ -559,11 +558,11 @@ bool OpenSslServerProcess::start(const std::vector& args) { std::vector mutableCmdline(cmdline.begin(), cmdline.end()); mutableCmdline.push_back('\0'); - STARTUPINFOA si {}; + STARTUPINFOA si{}; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; - PROCESS_INFORMATION pi {}; + PROCESS_INFORMATION pi{}; if (!CreateProcessA(nullptr, mutableCmdline.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) { return false; diff --git a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp index 17447dbc00e4..20f74b473e54 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp @@ -949,11 +949,7 @@ TEST_CASE("Numeric column widening getters align with Java TsFile", "[column]") TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool]") { CaseReporter cr("SessionPool basic"); SessionPoolBuilder poolBuilder; - poolBuilder.host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(3); + poolBuilder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->maxSize(3); auto pool = poolBuilder.build(); { @@ -991,11 +987,7 @@ TEST_CASE("SessionPool basic borrow/insert/query via RAII lease", "[sessionPool] TEST_CASE("SessionPool is safe under concurrent writers", "[sessionPool]") { CaseReporter cr("SessionPool concurrency"); SessionPoolBuilder poolBuilder; - poolBuilder.host("127.0.0.1") - ->rpcPort(6667) - ->username("root") - ->password("root") - ->maxSize(4); + poolBuilder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->maxSize(4); auto pool = poolBuilder.build(); { diff --git a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp index 48a9fc5790cf..a8fb9192066c 100644 --- a/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp +++ b/iotdb-client/client-cpp/test/tools/GenTlcpDualP12.cpp @@ -76,7 +76,7 @@ void addLocalKeyId(PKCS12_SAFEBAG* bag, X509* cert) { } } -void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG)* bags, X509* cert, EVP_PKEY* key, +void addCertAndKeyBags(STACK_OF(PKCS12_SAFEBAG) * bags, X509* cert, EVP_PKEY* key, const char* friendlyName, const std::string& password) { PKCS12_SAFEBAG* certbag = PKCS12_SAFEBAG_create_cert(cert); PKCS12_add_friendlyname_utf8(certbag, friendlyName, -1); @@ -143,7 +143,7 @@ int main(int argc, char** argv) { BIO* bio = BIO_new_file(outPath.c_str(), "wb"); if (bio == nullptr || i2d_PKCS12_bio(bio, p12) != 1) { - std::cerr << "failed to write " << outPath << "\n"; + std::cerr << "failed to write " << outPath << "\n"; BIO_free(bio); PKCS12_free(p12); return 5; diff --git a/iotdb-client/client-cpp/third-party/README.md b/iotdb-client/client-cpp/third-party/README.md index af0900fe6ee1..16c8923ddba4 100644 --- a/iotdb-client/client-cpp/third-party/README.md +++ b/iotdb-client/client-cpp/third-party/README.md @@ -68,7 +68,7 @@ Alternatively copy files manually from the URLs listed in | Platform | Typical files | |------------|---------------| -| `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-8.4-stable.tar.gz` (when `WITH_SSL=ON`, default) | +| `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`, `m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`, `tongsuo-0aed892c5f48c9a52d1f5667667ae45156b9cdf4.tar.gz` (when `WITH_SSL=ON`, default) | | `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz` (Xcode CLT usually provides m4/flex/bison) | | `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`, `win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison already on `PATH`) | From 1fdfbe7e75fcccc429d81a85a6e828473a95f9ac Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 10:35:09 +0800 Subject: [PATCH 18/24] fix(client-cpp): pin Tongsuo headers on macOS --- iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake index cbb5c55da831..6c93237a8c29 100644 --- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake +++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake @@ -233,10 +233,23 @@ set(OPENSSL_ROOT_DIR "${_tongsuo_inst}" CACHE PATH "Tongsuo install root" FORCE) set(OPENSSL_USE_STATIC_LIBS OFF) # Do not reuse paths cached by an earlier configure that resolved the system # OpenSSL. WITH_SSL requires Tongsuo because RpcSslUtils uses its TLCP APIs. -unset(OPENSSL_INCLUDE_DIR CACHE) +# +# OPENSSL_ROOT_DIR alone is not sufficient on macOS: Homebrew's /usr/local/include +# can still win FindOpenSSL's header search even while the libraries are resolved +# from OPENSSL_ROOT_DIR. That produces an unusable system-header/Tongsuo-library +# combination, so pin the headers to the Tongsuo installation as well. +set(OPENSSL_INCLUDE_DIR "${_tongsuo_inst}/include" + CACHE PATH "Tongsuo include directory" FORCE) unset(OPENSSL_SSL_LIBRARY CACHE) unset(OPENSSL_CRYPTO_LIBRARY CACHE) find_package(OpenSSL REQUIRED) +get_filename_component(_tongsuo_expected_include "${_tongsuo_inst}/include" REALPATH) +get_filename_component(_tongsuo_resolved_include "${OPENSSL_INCLUDE_DIR}" REALPATH) +if(NOT _tongsuo_resolved_include STREQUAL _tongsuo_expected_include) + message(FATAL_ERROR + "[Tongsuo] FindOpenSSL selected headers from ${OPENSSL_INCLUDE_DIR}; " + "expected ${_tongsuo_inst}/include") +endif() set(IOTDB_NTLS_RUNTIME_LIBRARIES "${OPENSSL_SSL_LIBRARY};${OPENSSL_CRYPTO_LIBRARY}" CACHE INTERNAL "NTLS provider runtime libraries" FORCE) From b7c6fdaf66bf4c7cf4005bab071a9e86e96f8d99 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 10:53:20 +0800 Subject: [PATCH 19/24] fix(client-cpp): prioritize Tongsuo headers --- iotdb-client/client-cpp/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iotdb-client/client-cpp/CMakeLists.txt b/iotdb-client/client-cpp/CMakeLists.txt index bfc441ffc4d0..88af804d2edd 100644 --- a/iotdb-client/client-cpp/CMakeLists.txt +++ b/iotdb-client/client-cpp/CMakeLists.txt @@ -192,6 +192,13 @@ target_include_directories(iotdb_session ${THRIFT_INCLUDE_DIR} $<$:${BOOST_INCLUDE_DIR}>) +if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") + # Homebrew installs Boost and OpenSSL into the same broad include prefix. + # Put Tongsuo first so Boost's -I prefix cannot shadow its NTLS headers. + set_target_properties(iotdb_session PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + target_include_directories(iotdb_session BEFORE PRIVATE "${OPENSSL_INCLUDE_DIR}") +endif() + if(APPLE) target_link_libraries(iotdb_session PRIVATE "-Wl,-force_load,${THRIFT_STATIC_LIB_PATH}") elseif(UNIX AND NOT MSVC) From 6a1dee60c81658a39fa0413495fae1c0f911798c Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 12:31:39 +0800 Subject: [PATCH 20/24] test(client-cpp): add IoTDB mutual TLS coverage --- iotdb-client/client-cpp/pom.xml | 2 +- iotdb-client/client-cpp/test/CMakeLists.txt | 17 ++++++ .../client-cpp/test/cpp/ItSslConnection.cpp | 26 +++++++++ .../client-cpp/test/cpp/ItSslConnection.h | 2 +- .../test/cpp/RpcSslIotdbE2eTest.cpp | 14 +++++ .../test/scripts/configure_iotdb_ssl_it.py | 56 ++++++++++++++++--- .../test/scripts/run_cpp_it_phases.py | 15 ++++- 7 files changed, 121 insertions(+), 11 deletions(-) diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index c6d7eddd3a96..19e77d75c422 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -148,7 +148,7 @@ - + cmake-run-test diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index 5cc7fe82acd8..b96cea52c732 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -31,6 +31,7 @@ if(CATCH2_INCLUDE_DIR) else() set(_catch2_include_dir "${CMAKE_CURRENT_BINARY_DIR}/catch2") endif() + set(_catch2_header "${_catch2_include_dir}/catch.hpp") if(NOT EXISTS "${_catch2_header}") file(MAKE_DIRECTORY "${_catch2_include_dir}") @@ -220,6 +221,17 @@ else() endforeach() endif() +if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") + if(MSVC) + add_test(NAME rpcSslMutualIotdbTest CONFIGURATIONS Release + COMMAND rpc_ssl_utils_tests "[iotdb][e2e]") + else() + add_test(NAME rpcSslMutualIotdbTest COMMAND rpc_ssl_utils_tests "[iotdb][e2e]") + endif() + set_tests_properties(rpcSslMutualIotdbTest PROPERTIES + ENVIRONMENT "IOTDB_CPP_SSL_MUTUAL_AUTH=1") +endif() + # Run sequentially: parallel ctest overloads the single local IoTDB instance. # sessionUtilsTest is a pure unit test and can run anytime. set_tests_properties( @@ -231,3 +243,8 @@ set_tests_properties( set_tests_properties( rpcNtlsUtilsTest PROPERTIES LABELS "ntls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +if(TEST rpcSslMutualIotdbTest) + set_tests_properties( + rpcSslMutualIotdbTest + PROPERTIES LABELS "mtls" RUN_SERIAL TRUE RESOURCE_LOCK iotdb_cpp_it_server) +endif() diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp index 4b9f15e76b33..077afa78e487 100644 --- a/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.cpp @@ -21,6 +21,7 @@ #if defined(WITH_SSL) && defined(IOTDB_RPC_SSL_IT) +#include #include #include @@ -104,6 +105,16 @@ std::string tlsTrustStorePath() { return path; } +std::string tlsKeyStorePath() { + static const std::string path = joinPath(joinPath(fixturesRoot(), "tls"), "tls-client.p12"); + return path; +} + +bool mutualTlsEnabled() { + const char* value = std::getenv("IOTDB_CPP_SSL_MUTUAL_AUTH"); + return value != nullptr && std::string(value) == "1"; +} + } // namespace void it_ssl_configure_tree_session(CSession* session) { @@ -113,6 +124,9 @@ void it_ssl_configure_tree_session(CSession* session) { ts_session_set_use_ssl(session, true); ts_session_set_ssl_protocol(session, "TLS"); ts_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); + if (mutualTlsEnabled()) { + ts_session_set_key_store(session, tlsKeyStorePath().c_str(), kStorePassword); + } } void it_ssl_configure_table_session(CTableSession* session) { @@ -122,6 +136,9 @@ void it_ssl_configure_table_session(CTableSession* session) { ts_table_session_set_use_ssl(session, true); ts_table_session_set_ssl_protocol(session, "TLS"); ts_table_session_set_trust_store(session, tlsTrustStorePath().c_str(), kStorePassword); + if (mutualTlsEnabled()) { + ts_table_session_set_key_store(session, tlsKeyStorePath().c_str(), kStorePassword); + } } namespace itssl { @@ -131,6 +148,9 @@ void configureSessionBuilder(SessionBuilder& builder) { ->sslProtocol("TLS") ->trustStore(tlsTrustStorePath()) ->trustStorePwd(kStorePassword); + if (mutualTlsEnabled()) { + builder.keyStore(tlsKeyStorePath())->keyStorePwd(kStorePassword); + } } void configureTableSessionBuilder(TableSessionBuilder& builder) { @@ -138,6 +158,9 @@ void configureTableSessionBuilder(TableSessionBuilder& builder) { ->sslProtocol("TLS") ->trustStore(tlsTrustStorePath()) ->trustStorePwd(kStorePassword); + if (mutualTlsEnabled()) { + builder.keyStore(tlsKeyStorePath())->keyStorePwd(kStorePassword); + } } void configureSessionPoolBuilder(SessionPoolBuilder& builder) { @@ -145,6 +168,9 @@ void configureSessionPoolBuilder(SessionPoolBuilder& builder) { ->sslProtocol("TLS") ->trustStore(tlsTrustStorePath()) ->trustStorePwd(kStorePassword); + if (mutualTlsEnabled()) { + builder.keyStore(tlsKeyStorePath())->keyStorePwd(kStorePassword); + } } std::shared_ptr newOpenedTreeSession() { diff --git a/iotdb-client/client-cpp/test/cpp/ItSslConnection.h b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h index 9818fd076439..ac13c02e8d12 100644 --- a/iotdb-client/client-cpp/test/cpp/ItSslConnection.h +++ b/iotdb-client/client-cpp/test/cpp/ItSslConnection.h @@ -36,7 +36,7 @@ extern "C" { #endif -/** Apply one-way TLS settings for integration tests against a TLS-enabled IoTDB. */ +/** Apply TLS settings for integration tests, including a client key store in mutual TLS mode. */ void it_ssl_configure_tree_session(CSession* session); void it_ssl_configure_table_session(CTableSession* session); diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp index a39e26325d42..36cbd73b5326 100644 --- a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp +++ b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp @@ -179,4 +179,18 @@ TEST_CASE("Plain client cannot connect to TLS-enabled IoTDB", "[rpc][ssl][iotdb] REQUIRE_THROWS_AS(session.open(false), IoTDBException); } +TEST_CASE("TLS client without a key store cannot connect to mutual TLS IoTDB", + "[.][rpc][ssl][iotdb][mutual][e2e]") { + SessionBuilder builder; + builder.host("127.0.0.1") + ->rpcPort(6667) + ->username("root") + ->password("root") + ->useSSL(true) + ->sslProtocol("TLS") + ->trustStore(ssltest::tlsFixture("tls-trust.p12")) + ->trustStorePwd(ssltest::kStorePassword); + REQUIRE_THROWS(builder.build()); +} + #endif // WITH_SSL && IOTDB_RPC_SSL_IT diff --git a/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py index 79c5a19de111..5951fbc57dbb 100644 --- a/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py +++ b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py @@ -29,6 +29,7 @@ STORE_PASSWORD = "thrift" SERVER_PKCS12 = "tls-server.p12" +SERVER_TRUSTSTORE = "tls-server-trust.p12" def replace_property(text: str, key: str, value: str) -> str: @@ -71,7 +72,7 @@ def configure_plain(dist_root: Path) -> int: return 0 -def configure_tls(dist_root: Path, fixtures_root: Path) -> int: +def configure_tls(dist_root: Path, fixtures_root: Path, require_client_auth: bool) -> int: props_path = dist_root / "conf" / "iotdb-system.properties" if not props_path.is_file(): print(f"iotdb-system.properties not found: {props_path}", file=sys.stderr) @@ -86,24 +87,61 @@ def configure_tls(dist_root: Path, fixtures_root: Path) -> int: shutil.copy2(source, ssl_dir / SERVER_PKCS12) key_store = (ssl_dir / SERVER_PKCS12).as_posix() + trust_store = "" + if require_client_auth: + ca_cert = fixtures_root / "tls" / "ca.crt" + if not ca_cert.is_file(): + print(f"fixture missing: {ca_cert}", file=sys.stderr) + return 1 + keytool = shutil.which("keytool") + if keytool is None: + print("keytool not found; a JDK is required for the mutual TLS IT", file=sys.stderr) + return 1 + trust_store_path = ssl_dir / SERVER_TRUSTSTORE + trust_store_path.unlink(missing_ok=True) + subprocess.run( + [ + keytool, + "-importcert", + "-noprompt", + "-alias", + "cpp-ssl-it-ca", + "-file", + str(ca_cert), + "-keystore", + str(trust_store_path), + "-storetype", + "PKCS12", + "-storepass", + STORE_PASSWORD, + ], + check=True, + ) + trust_store = trust_store_path.as_posix() text = props_path.read_text(encoding="utf-8") text = replace_property(text, "enable_thrift_ssl", "true") - text = replace_property(text, "thrift_ssl_client_auth", "false") + text = replace_property( + text, "thrift_ssl_client_auth", str(require_client_auth).lower() + ) text = replace_property(text, "key_store_path", key_store) text = replace_property(text, "key_store_pwd", STORE_PASSWORD) - text = replace_property(text, "trust_store_path", "") - text = replace_property(text, "trust_store_pwd", "") + text = replace_property(text, "trust_store_path", trust_store) + text = replace_property( + text, "trust_store_pwd", STORE_PASSWORD if require_client_auth else "" + ) text = replace_property(text, "ssl_protocol", "TLS") props_path.write_text(text, encoding="utf-8", newline="\n") - print(f"Configured TLS IT server properties in {props_path}") + mode = "mutual TLS" if require_client_auth else "TLS" + print(f"Configured {mode} IT server properties in {props_path}") return 0 def main() -> int: if len(sys.argv) < 3: print( - "usage: configure_iotdb_ssl_it.py [enable|disable]", + "usage: configure_iotdb_ssl_it.py " + "[enable|mutual|disable]", file=sys.stderr, ) return 2 @@ -118,7 +156,11 @@ def main() -> int: if mode in ("enable", "tls", "on"): stop_iotdb(dist_root) - return configure_tls(dist_root, fixtures_root) + return configure_tls(dist_root, fixtures_root, False) + + if mode in ("mutual", "mtls"): + stop_iotdb(dist_root) + return configure_tls(dist_root, fixtures_root, True) print(f"unknown mode: {mode}", file=sys.stderr) return 2 diff --git a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py index 020f98061fcd..9b068014eebd 100644 --- a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py +++ b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -"""Run C++ client integration tests in two IoTDB modes: plain then TLS.""" +"""Run C++ client integration tests against plain, TLS, and mutual TLS IoTDB.""" from __future__ import annotations @@ -107,7 +107,18 @@ def main() -> int: finally: stop_iotdb(dist_root) - print("=== Phase 2b: NTLS (no IoTDB; openssl s_server) ===") + print("=== Phase 3: restart IoTDB with mutual TLS ===") + run( + [sys.executable, str(configure), str(dist_root), str(fixtures_root), "mutual"], + cwd=scripts_root, + ) + try: + start_iotdb(dist_root, start_script, args.wait_seconds) + run(ctest_base + ["-L", "mtls"], build_dir) + finally: + stop_iotdb(dist_root) + + print("=== Phase 4: NTLS (no IoTDB; openssl s_server) ===") run(ctest_base + ["-L", "ntls"], build_dir) print("All C++ integration test phases passed.") From ef084fc5938d4acedde3c90f82650599f110a9a4 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 12:37:18 +0800 Subject: [PATCH 21/24] build: exclude C++ TLS fixtures from RAT --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 90ac0c70ce7c..4d158a7c8621 100644 --- a/pom.xml +++ b/pom.xml @@ -766,6 +766,8 @@ **/package-metadata/third_party/NOTICE **/package-metadata/third_party/licenses/** + + **/client-cpp/test/fixtures/** hadoopbin windowssystem32 From 301e2edd47c7ea86577d0903ad029dcf76dcab88 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 12:58:53 +0800 Subject: [PATCH 22/24] fix(client-cpp): prioritize Tongsuo headers in tests --- iotdb-client/client-cpp/test/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt b/iotdb-client/client-cpp/test/CMakeLists.txt index b96cea52c732..a8c55d95216f 100644 --- a/iotdb-client/client-cpp/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/test/CMakeLists.txt @@ -87,6 +87,11 @@ foreach(_t IN LISTS _test_targets) if(BOOST_INCLUDE_DIR) target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") endif() + if(WITH_SSL AND IOTDB_NTLS_PROVIDER STREQUAL "TONGSUO") + # Keep Homebrew OpenSSL headers from shadowing Tongsuo for test-only sources. + set_target_properties(${_t} PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + target_include_directories(${_t} BEFORE PRIVATE "${OPENSSL_INCLUDE_DIR}") + endif() target_link_libraries(${_t} PRIVATE iotdb_session) if(WITH_SSL) if(IOTDB_NTLS_PROVIDER STREQUAL "GMSSL") From ccc469653510019cef9f255da1c9ffe43f715222 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 13:18:19 +0800 Subject: [PATCH 23/24] fix(client-cpp): prioritize Tongsuo headers in examples --- iotdb-client/client-cpp/examples/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iotdb-client/client-cpp/examples/CMakeLists.txt b/iotdb-client/client-cpp/examples/CMakeLists.txt index 4d7aaa86cf06..ed830970b0d6 100644 --- a/iotdb-client/client-cpp/examples/CMakeLists.txt +++ b/iotdb-client/client-cpp/examples/CMakeLists.txt @@ -270,6 +270,9 @@ if(_iotdb_examples_in_tree AND WITH_SSL AND IOTDB_EXAMPLES_REGISTER_TESTS) if(BOOST_INCLUDE_DIR) target_include_directories(${_t} PRIVATE "${BOOST_INCLUDE_DIR}") endif() + # Keep Homebrew OpenSSL headers from shadowing Tongsuo for the NTLS helpers. + set_target_properties(${_t} PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + target_include_directories(${_t} BEFORE PRIVATE "${OPENSSL_INCLUDE_DIR}") file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" _iotdb_openssl_root_dir_cmake) string(REPLACE "\\" "/" _iotdb_openssl_root_dir_fwd "${_iotdb_openssl_root_dir_cmake}") if(WIN32) From 6fc2f34614a31450b1c6dd684885de9d4efdaa7f Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 8 Sep 2026 13:45:33 +0800 Subject: [PATCH 24/24] build: exclude TLS fixtures from child RAT scans --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 4d158a7c8621..6c3139505dd4 100644 --- a/pom.xml +++ b/pom.xml @@ -768,6 +768,9 @@ **/package-metadata/third_party/licenses/** **/client-cpp/test/fixtures/** + + test/fixtures/**/*.crt + test/fixtures/**/*.key hadoopbin windowssystem32