diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c7b634c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/libssl.yaml b/.github/workflows/libssl.yaml new file mode 100644 index 0000000..789e771 --- /dev/null +++ b/.github/workflows/libssl.yaml @@ -0,0 +1,156 @@ +name: rustls-libssl + +permissions: + contents: read + +on: + push: + pull_request: + merge_group: + schedule: + - cron: '15 12 * * 3' + +defaults: + run: + working-directory: rustls-libssl + +jobs: + build: + name: Build+test + runs-on: ${{ matrix.os }} + strategy: + matrix: + rust: + - stable + - beta + - nightly + os: [ubuntu-latest] + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install build dependencies + run: sudo apt-get update && sudo apt-get install -y openssl libssl3 libssl-dev lld + + - name: Install ${{ matrix.rust }} toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + + - run: make PROFILE=release test + + valgrind: + name: Valgrind + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Install valgrind + run: sudo apt-get update && sudo apt-get install -y valgrind + - name: Install build dependencies + run: sudo apt-get update && sudo apt-get install -y openssl libssl3 libssl-dev lld + - run: export VALGRIND="valgrind -q" + - run: make test + + docs: + name: Check for documentation errors + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install build dependencies + run: sudo apt-get update && sudo apt-get install -y openssl libssl3 libssl-dev lld + + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: cargo doc (all features) + run: cargo doc --all-features --no-deps --workspace + env: + RUSTDOCFLAGS: -Dwarnings + + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.67.1 + components: rustfmt + - name: Check Rust formatting + run: cargo fmt --all -- --check + - name: Check src/entry.rs formatting + run: ./admin/format --all -- --check + - name: Check C formatting + run: make format-check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Check clippy + # We allow unknown lints here because sometimes the nightly job + # (below) will have a new lint that we want to suppress. + # If we suppress (e.g. #![allow(clippy::arc_with_non_send_sync)]), + # we would get an unknown-lint error from older clippy versions. + run: cargo clippy --locked --workspace -- -D warnings -A unknown-lints + + clippy-nightly-optional: + name: Clippy nightly (optional) + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - name: Check clippy + run: cargo clippy --locked --workspace -- -D warnings + + clang-tidy: + name: Clang Tidy + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Clang tidy + run: clang-tidy tests/*.c -- -I src/ + + miri: + name: Miri + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install nightly Rust + uses: dtolnay/rust-toolchain@nightly + - run: rustup override set "nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)" + - run: rustup component add miri + - run: cargo miri test diff --git a/README.md b/README.md new file mode 100644 index 0000000..0165df5 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# rustls-openssl-compat + +This is the planned home of several rustls ↔️ OpenSSL compatibility layers. +Currently here: + +- **rustls-libssl**: an implementation of the OpenSSL libssl ABI in terms of rustls. + +Not yet here: + +- **rustls-libcrypto**: an implementation of rustls `CryptoProvider` in terms of OpenSSL's libcrypto. diff --git a/rustls-libssl/.gitignore b/rustls-libssl/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/rustls-libssl/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/rustls-libssl/Cargo.lock b/rustls-libssl/Cargo.lock new file mode 100644 index 0000000..4e8f674 --- /dev/null +++ b/rustls-libssl/Cargo.lock @@ -0,0 +1,334 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "cc" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-libssl" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", + "openssl-sys", + "rustls", +] + +[[package]] +name = "rustls-pki-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/rustls-libssl/Cargo.toml b/rustls-libssl/Cargo.toml new file mode 100644 index 0000000..2fac7a5 --- /dev/null +++ b/rustls-libssl/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustls-libssl" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[lib] +name = "ssl" +crate-type = ["cdylib"] + +[dependencies] +env_logger = "0.10" +log = "0.4" +openssl-sys = "0.9.98" +rustls = "0.22" diff --git a/rustls-libssl/LICENSE b/rustls-libssl/LICENSE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/rustls-libssl/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed 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. diff --git a/rustls-libssl/Makefile b/rustls-libssl/Makefile new file mode 100644 index 0000000..0a2248e --- /dev/null +++ b/rustls-libssl/Makefile @@ -0,0 +1,52 @@ +CARGO ?= cargo +CARGOFLAGS += --locked + +CFLAGS := -Werror -Wall -Wextra -Wpedantic -g $(shell pkg-config --cflags openssl) +PROFILE := debug + +ifeq ($(CC), clang) + CFLAGS += -fsanitize=address -fsanitize=undefined + LDFLAGS += -fsanitize=address +endif + +ifeq ($(PROFILE), release) + CFLAGS += -O3 + CARGOFLAGS += --release +endif + +ifneq (,$(TARGET)) + PROFILE := $(TARGET)/$(PROFILE) + CARGOFLAGS += --target $(TARGET) +endif + +all: target/$(PROFILE)/libssl.so.3 + +test: all + ${CARGO} test --locked + +integration: all + ${CARGO} test --locked -- --ignored + +target: + mkdir -p $@ + +target/$(PROFILE)/libssl.so.3: target/$(PROFILE)/libssl.so + cp -v $^ $@ + +target/$(PROFILE)/libssl.so: *.rs src/*.rs Cargo.toml + ${CARGO} build $(CARGOFLAGS) + +clean: + rm -rf target + +format: + find . \ + -name '*.[c|h]' | \ + xargs clang-format -i + +format-check: + find . \ + -name '*.[c|h]' | \ + xargs clang-format --dry-run -Werror -i + +.PHONY: all clean test integration format format-check diff --git a/rustls-libssl/admin/format b/rustls-libssl/admin/format new file mode 100755 index 0000000..aecb4e6 --- /dev/null +++ b/rustls-libssl/admin/format @@ -0,0 +1,16 @@ +#!/bin/sh + +# rustfmt cannot generally format the inside of macro invocations, +# because they can invent their own syntax. +# +# in the case of the `entry!` macro in src/entry.rs, we specifically +# try and keep the syntax the same as a rust function definition. +# +# that means we can trick rustfmt into formatting it by replacing +# `entry!` with `mod entry`, and then restore it back afterwards. + +sed -i -e 's/^entry! {/mod entry {/g' src/entry.rs +cargo fmt "$@" +rc=$? +sed -i -e 's/^mod entry {/entry! {/g' src/entry.rs +exit $rc diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs new file mode 100644 index 0000000..500758f --- /dev/null +++ b/rustls-libssl/build.rs @@ -0,0 +1,55 @@ +use std::{env, fs, path}; + +fn main() { + if cfg!(target_os = "linux") { + println!("cargo:rustc-cdylib-link-arg=-Wl,--soname=libssl.so.3"); + + // We require lld, because ld only supports one --version-script + // and rustc uses it for its own purposes (and provides no API for us). + println!("cargo:rustc-cdylib-link-arg=-fuse-ld=lld"); + + let filename = write_version_file(); + println!("cargo:rustc-cdylib-link-arg=-Wl,--version-script={filename}"); + + for symbol in ENTRYPOINTS { + // Rename underscore-prefixed symbols (produced by rust code) to + // unprefixed symbols (manipulated by our version file). + println!( + "cargo:rustc-cdylib-link-arg=-Wl,--defsym={}=_{}", + symbol, symbol + ); + } + } +} + +fn write_version_file() -> String { + let out_dir = env::var("OUT_DIR").unwrap(); + let dest = path::Path::new(&out_dir).join("versions.map"); + + let mut content = String::new(); + content.push_str("OPENSSL_3.0.0 {\n"); + content.push_str(" global:\n"); + for e in ENTRYPOINTS { + content.push_str(&format!(" {e};\n")); + } + content.push_str(" local:\n"); + content.push_str(" *;\n"); + content.push_str("};\n"); + + fs::write(&dest, content).unwrap(); + println!("cargo:rerun-if-changed=build.rs"); + dest.to_str().unwrap().to_string() +} + +const ENTRYPOINTS: &[&str] = &[ + "OPENSSL_init_ssl", + "SSL_CTX_free", + "SSL_CTX_new", + "SSL_CTX_up_ref", + "SSL_free", + "SSL_new", + "SSL_up_ref", + "TLS_client_method", + "TLS_method", + "TLS_server_method", +]; diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs new file mode 100644 index 0000000..9702cc5 --- /dev/null +++ b/rustls-libssl/src/entry.rs @@ -0,0 +1,177 @@ +//! This file contains all the libssl entrypoints that we implement. +//! +//! It should mainly be concerned with mapping these calls up to +//! the safe APIs implemented elsewhere. + +use core::mem; +use std::os::raw::c_int; +use std::sync::Mutex; + +use crate::error::{ffi_panic_boundary, Error}; +use crate::ffi::{ + free_arc, to_arc_mut_ptr, try_clone_arc, try_ref_from_ptr, Castable, OwnershipArc, OwnershipRef, +}; + +/// Makes a entry function definition. +/// +/// The body is wrapped in `ffi_panic_boundary`, the name is `#[no_mangle]`, +/// and is `extern "C"`. +/// +/// See also `build.rs`: +/// +/// - the name should start with `_` to support the linker-renaming and symbol +/// versioning happening there, +/// - the name should appear in the list of all entry points there. +macro_rules! entry { + (pub fn $name:ident($($args:tt)*) $body:block) => { + #[no_mangle] + pub extern "C" fn $name($($args)*) { ffi_panic_boundary! { $body } } + }; + (pub fn $name:ident($($args:tt)*) -> $ret:ty $body:block) => { + #[no_mangle] + pub extern "C" fn $name($($args)*) -> $ret { ffi_panic_boundary! { $body } } + }; +} + +pub struct OpenSslInitSettings; +type OPENSSL_INIT_SETTINGS = OpenSslInitSettings; + +entry! { + pub fn _OPENSSL_init_ssl(_opts: u64, settings: *const OPENSSL_INIT_SETTINGS) -> c_int { + const VERSION: &str = env!("CARGO_PKG_VERSION"); + + if !settings.is_null() { + return Error::not_supported("settings").raise().into(); + } + env_logger::init(); + log::trace!("OPENSSL_init_ssl in rustls-libssl {VERSION}"); + C_INT_SUCCESS + } +} + +type SSL_METHOD = crate::SslMethod; + +entry! { + pub fn _TLS_method() -> *const SSL_METHOD { + &crate::TLS_METHOD + } +} + +entry! { + pub fn _TLS_server_method() -> *const SSL_METHOD { + &crate::TLS_SERVER_METHOD + } +} + +entry! { + pub fn _TLS_client_method() -> *const SSL_METHOD { + &crate::TLS_CLIENT_METHOD + } +} + +impl Castable for SSL_METHOD { + type Ownership = OwnershipRef; + type RustType = SSL_METHOD; +} + +type SSL_CTX = crate::SslContext; + +entry! { + pub fn _SSL_CTX_new(meth: *const SSL_METHOD) -> *mut SSL_CTX { + let method = try_ref_from_ptr!(meth); + to_arc_mut_ptr(Mutex::new(crate::SslContext::new(method))) + } +} + +entry! { + pub fn _SSL_CTX_up_ref(ctx: *mut SSL_CTX) -> c_int { + let ctx = try_clone_arc!(ctx); + mem::forget(ctx.clone()); + C_INT_SUCCESS + } +} + +entry! { + pub fn _SSL_CTX_free(ctx: *mut SSL_CTX) { + free_arc(ctx); + } +} + +impl Castable for SSL_CTX { + type Ownership = OwnershipArc; + type RustType = Mutex; +} + +type SSL = crate::Ssl; + +entry! { + pub fn _SSL_new(ctx: *mut SSL_CTX) -> *mut SSL { + let ctx = try_clone_arc!(ctx); + to_arc_mut_ptr(Mutex::new(crate::Ssl::new(ctx))) + } +} + +entry! { + pub fn _SSL_up_ref(ssl: *mut SSL) -> c_int { + let ssl = try_clone_arc!(ssl); + mem::forget(ssl.clone()); + C_INT_SUCCESS + } +} + +entry! { + pub fn _SSL_free(ssl: *mut SSL) { + free_arc(ssl); + } +} + +impl Castable for SSL { + type Ownership = OwnershipArc; + type RustType = Mutex; +} + +/// Normal OpenSSL return value convention success indicator. +const C_INT_SUCCESS: c_int = 1; + +#[cfg(test)] +mod tests { + use super::*; + use core::ptr; + + #[test] + fn test_SSL_CTX_new_null() { + assert!(_SSL_CTX_new(ptr::null()).is_null()); + } + + #[test] + fn test_SSL_new_null() { + assert!(_SSL_new(ptr::null_mut()).is_null()); + } + + #[test] + fn test_SSL_up_ref_null() { + assert_eq!(_SSL_up_ref(ptr::null_mut()), 0); + } + + #[test] + fn test_SSL_free() { + let ctx = _SSL_CTX_new(_TLS_method()); + assert!(!ctx.is_null()); + let ssl = _SSL_new(ctx); + assert!(!ssl.is_null()); + _SSL_free(ssl); + _SSL_CTX_free(ctx); + } + + #[test] + fn test_SSL_free_after_up_ref() { + let ctx = _SSL_CTX_new(_TLS_method()); + assert!(!ctx.is_null()); + let ssl = _SSL_new(ctx); + assert!(!ssl.is_null()); + assert_eq!(_SSL_up_ref(ssl), 1); + _SSL_free(ssl); // ref 2 + _SSL_free(ssl); // ref 1 + _SSL_CTX_free(ctx); + } +} diff --git a/rustls-libssl/src/error.rs b/rustls-libssl/src/error.rs new file mode 100644 index 0000000..a10ab69 --- /dev/null +++ b/rustls-libssl/src/error.rs @@ -0,0 +1,194 @@ +use core::ffi::{c_int, c_long}; +use core::ptr; +use std::ffi::{CStr, CString}; + +use openssl_sys::{ERR_new, ERR_set_error, ERR_RFLAGS_OFFSET, ERR_RFLAG_FATAL}; + +// See openssl/err.h for the source of these magic numbers. + +#[derive(Copy, Clone, Debug)] +#[repr(i32)] +enum Lib { + /// This is `ERR_LIB_SSL`. + Ssl = 20, + + /// This is `ERR_LIB_USER`. + User = 128, +} + +const ERR_RFLAG_COMMON: i32 = 0x2i32 << ERR_RFLAGS_OFFSET; + +#[derive(Copy, Clone, Debug)] +#[repr(i32)] +enum Reason { + PassedNullParameter = (ERR_RFLAG_FATAL as i32) | ERR_RFLAG_COMMON | 258, + InternalError = (ERR_RFLAG_FATAL as i32) | ERR_RFLAG_COMMON | 259, + UnableToGetWriteLock = (ERR_RFLAG_FATAL as i32) | ERR_RFLAG_COMMON | 272, + OperationFailed = (ERR_RFLAG_FATAL as i32) | ERR_RFLAG_COMMON | 263, + Unsupported = ERR_RFLAG_COMMON | 268, +} + +#[derive(Debug)] +pub struct Error { + lib: Lib, + reason: Reason, + string: Option, +} + +impl Error { + pub fn unexpected_panic() -> Self { + Self { + lib: Lib::Ssl, + reason: Reason::InternalError, + string: None, + } + } + + pub fn null_pointer() -> Self { + Self { + lib: Lib::Ssl, + reason: Reason::PassedNullParameter, + string: None, + } + } + + pub fn cannot_lock() -> Self { + Self { + lib: Lib::Ssl, + reason: Reason::UnableToGetWriteLock, + string: None, + } + } + + pub fn not_supported(hint: &str) -> Self { + Self { + lib: Lib::Ssl, + reason: Reason::Unsupported, + string: Some(hint.to_string()), + } + } + + pub fn bad_data(hint: &str) -> Self { + Self { + lib: Lib::Ssl, + reason: Reason::OperationFailed, + string: Some(hint.to_string()), + } + } + + pub fn from_rustls(err: rustls::Error) -> Self { + Self { + lib: Lib::User, + reason: Reason::OperationFailed, + string: Some(err.to_string()), + } + } + + pub fn from_io(err: std::io::Error) -> Self { + Self { + lib: Lib::User, + reason: Reason::OperationFailed, + string: Some(err.to_string()), + } + } + + /// Add this error to the openssl error stack. + pub fn raise(self) -> Self { + log::error!("raising {self:?}"); + let cstr = CString::new( + self.string + .clone() + .unwrap_or_else(|| format!("{:?}", self.reason)), + ) + .unwrap(); + // safety: b"%s\0" satisfies requirements of from_bytes_with_nul_unchecked. + let fmt = unsafe { CStr::from_bytes_with_nul_unchecked(b"%s\0") }; + unsafe { + ERR_new(); + // nb. miri cannot do variadic functions, so we define a miri-only equivalent + #[cfg(not(miri))] + ERR_set_error( + self.lib as c_int, + self.reason as c_int, + fmt.as_ptr(), + cstr.as_ptr(), + ); + #[cfg(miri)] + crate::miri::ERR_set_error(self.lib as c_int, self.reason as c_int, cstr.as_ptr()); + } + self + } +} + +// These conversions determine how errors are reported from entry point +// functions. + +impl From for *const T { + fn from(_: Error) -> Self { + ptr::null() + } +} + +impl From for *mut T { + fn from(_: Error) -> Self { + ptr::null_mut() + } +} + +impl From for c_int { + fn from(_: Error) -> Self { + // for typical OpenSSL functions (return 0 on error) + 0 + } +} + +impl From for c_long { + fn from(_: Error) -> Self { + // ditto + 0 + } +} + +impl From for u64 { + fn from(_: Error) -> Self { + // for options functions (return 0 on error) + 0 + } +} + +impl From for u32 { + fn from(_: Error) -> Self { + // for `SSL_CIPHER_get_id` + 0 + } +} + +impl From for u16 { + fn from(_: Error) -> Self { + // for `SSL_CIPHER_get_protocol_id` + 0 + } +} + +impl From for () { + fn from(_: Error) { + // for void functions (return early on error) + } +} + +#[macro_export] +macro_rules! ffi_panic_boundary { + ( $($tt:tt)* ) => { + match ::std::panic::catch_unwind( + ::std::panic::AssertUnwindSafe(|| { + $($tt)* + })) { + Ok(ret) => ret, + Err(_) => return $crate::error::Error::unexpected_panic() + .raise() + .into(), + } + } +} + +pub(crate) use ffi_panic_boundary; diff --git a/rustls-libssl/src/ffi.rs b/rustls-libssl/src/ffi.rs new file mode 100644 index 0000000..d19ad08 --- /dev/null +++ b/rustls-libssl/src/ffi.rs @@ -0,0 +1,451 @@ +//! Violently borrowed from rustls-ffi. +//! +//! TODO: undo that. + +use core::ffi::{c_char, CStr}; +use std::mem; +use std::sync::Arc; + +/// Used to mark that pointer to a [`Castable`]'s underlying `Castable::RustType` is provided +/// to C code as a pointer to a `Box`. +pub(crate) struct OwnershipBox; + +/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided +/// to C code as a pointer to an `Arc`. +pub(crate) struct OwnershipArc; + +/// Used to mark that a pointer to a [`Castable`]'s underlying `Castable::RustType` is provided +/// to C code as a pointer to a reference, `&Castable::RustType`. +pub(crate) struct OwnershipRef; + +/// A trait for marking the type of a pointer to a [`Castable`]'s underlying `Castable::RustType` +/// that is provided to C code, either a [`OwnershipBox`] when it is a pointer to a `Box<_>`, +/// a [`OwnershipArc`] when it is a pointer to an `Arc<_>`, or a [`OwnershipRef`] when it is a +/// pointer to a `&_`. +pub(crate) trait OwnershipMarker {} + +impl OwnershipMarker for OwnershipBox {} + +impl OwnershipMarker for OwnershipArc {} + +impl OwnershipMarker for OwnershipRef {} + +/// `Castable` represents the relationship between a snake case type (like [`client::rustls_client_config`]) +/// and the corresponding Rust type (like [`rustls::ClientConfig`]), specified as the associated type +/// `RustType`. Each `Castable` also has an associated type `Ownership` specifying one of the +/// [`OwnershipMarker`] types, [`OwnershipBox`], [`OwnershipArc`] or [`OwnershipRef`]. +/// +/// An implementation of `Castable` that uses [`OwnershipBox`] indicates that when we give C code +/// a pointer to the relevant `RustType` `T`, that it is actually a `Box`. An +/// implementation of `Castable` that uses [`OwnershipArc`] means that when we give C code a +/// pointer to the relevant type, that it is actually an `Arc`. Lastly an implementation of +/// `Castable` that uses [`OwnershipRef`] means that when we give C code a pointer to the relevant +/// type, that it is actually a `&T`. +/// +/// By using an associated type on `Castable` to communicate this we can use the type system to +/// guarantee that a single type can't implement `Castable` for more than one [`OwnershipMarker`], +/// since this would be a conflicting trait implementation and rejected by the compiler. +/// +/// This trait allows us to avoid using `as` in most places, and ensures that when we cast, we're +/// preserving const-ness, and casting between the correct types. Implementing this is required in +/// order to use `try_ref_from_ptr!` or `try_mut_from_ptr!` and several other helpful cast-related +/// conversion helpers. +pub(crate) trait Castable { + /// Indicates whether to use `Box` or `Arc` when giving a pointer to C code for the underlying + /// `RustType`. + type Ownership: OwnershipMarker; + + /// The underlying Rust type that we are casting to and from. + type RustType; +} + +/// Convert a const pointer to a [`Castable`] to a const pointer to its underlying +/// [`Castable::RustType`]. +/// +/// This can be used regardless of the [`Castable::Ownership`] as we can make const pointers for +/// `Box`, `Arc` and ref types. +pub(crate) fn cast_const_ptr(ptr: *const C) -> *const C::RustType +where + C: Castable, +{ + ptr as *const _ +} + +/// Convert a [`Castable`]'s underlying [`Castable::RustType`] to a constant pointer +/// to an `Arc` over the rust type. Can only be used when the `Castable` has specified a cast type +/// equal to [`OwnershipArc`]. +pub(crate) fn to_arc_const_ptr(src: C::RustType) -> *const C +where + C: Castable, +{ + Arc::into_raw(Arc::new(src)) as *const _ +} + +/// Convert a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer +/// to an `Arc` over the rust type. Can only be used when the `Castable` has specified a cast type +/// equal to [`OwnershipArc`]. +pub(crate) fn to_arc_mut_ptr(src: C::RustType) -> *mut C +where + C: Castable, +{ + Arc::into_raw(Arc::new(src)) as *mut C +} + +/// Given a const pointer to a [`Castable`] representing an `Arc`, clone the `Arc` and return +/// the corresponding Rust type. +/// +/// The caller still owns its copy of the `Arc`. In other words, the reference count of the +/// `Arc` will be incremented by 1 by the end of this function. +/// +/// To achieve that, we need to `mem::forget` the `Arc` we get back from `into_raw`, because +/// `into_raw` _does_ take back ownership. If we called `into_raw` without `mem::forget`, at the +/// end of the function that Arc would be dropped and the reference count would be decremented, +/// potentially to 0, causing memory to be freed. +/// +/// Does nothing, returning `None`, when passed a `NULL` pointer. Can only be used when the +/// `Castable` has specified a cast type equal to [`OwnershipArc`]. +/// +/// ## Unsafety: +/// +/// If non-null, `ptr` must be a pointer that resulted from previously calling `Arc::into_raw`, +/// e.g. from using [`to_arc_const_ptr`]. +pub(crate) fn clone_arc(ptr: *const C) -> Option> +where + C: Castable, +{ + if ptr.is_null() { + return None; + } + let rs_typed = cast_const_ptr::(ptr); + let r = unsafe { Arc::from_raw(rs_typed) }; + let val = Arc::clone(&r); + mem::forget(r); + Some(val) +} + +/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying rust type. +/// +/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has +/// specified a cast type equal to [`OwnershipBox`]. +/// +/// ## Unsafety: +/// +/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`, +/// e.g. from using [`to_boxed_mut_ptr`]. +pub(crate) fn to_box(ptr: *mut C) -> Option> +where + C: Castable, +{ + if ptr.is_null() { + return None; + } + let rs_typed = cast_mut_ptr(ptr); + unsafe { Some(Box::from_raw(rs_typed)) } +} + +/// Free a constant pointer to a [`Castable`]'s underlying [`Castable::RustType`] by +/// reconstituting an `Arc` from the raw pointer and dropping it. +/// +/// For types represented with an `Arc` on the Rust side, we offer a `_free()` +/// method to the C side that decrements the refcount and ultimately drops +/// the `Arc` if the refcount reaches 0. By contrast with `to_arc`, we call +/// `Arc::from_raw` on the input pointer, but we _don't_ clone it, because we +/// want the refcount to be lower by one when we reach the end of the function. +/// +/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has +/// specified a cast type equal to [`OwnershipArc`]. +pub(crate) fn free_arc(ptr: *const C) +where + C: Castable, +{ + if ptr.is_null() { + return; + } + let rs_typed = cast_const_ptr(ptr); + drop(unsafe { Arc::from_raw(rs_typed) }); +} + +/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying +/// [`Castable::RustType`], and immediately let it fall out of scope to be freed. +/// +/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`]. +/// +/// ## Unsafety: +/// +/// If non-null, `ptr` must be a pointer that resulted from previously calling `Box::into_raw`, +/// e.g. from using [`to_boxed_mut_ptr`]. +pub(crate) fn free_box(ptr: *mut C) +where + C: Castable, +{ + to_box(ptr); +} + +/// Convert a mutable pointer to a [`Castable`] to a mutable pointer to its underlying +/// [`Castable::RustType`]. +/// +/// Can only be used when the `Castable` has specified a cast source equal to `BoxCastPtrMarker`. +pub(crate) fn cast_mut_ptr(ptr: *mut C) -> *mut C::RustType +where + C: Castable, +{ + ptr as *mut _ +} + +/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer +/// to a `Box` over the rust type. +/// +/// Can only be used when the `Castable` has specified a cast type equal to [`OwnershipBox`]. +pub(crate) fn to_boxed_mut_ptr(src: C::RustType) -> *mut C +where + C: Castable, +{ + Box::into_raw(Box::new(src)) as *mut _ +} + +/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a mutable pointer +/// to a `Box` over the rust type and sets the `dst` out pointer to the resulting mutable `Box` +/// pointer. See [`to_boxed_mut_ptr`] for more information. +/// +/// ## Unsafety: +/// +/// `dst` must not be `NULL`. +pub(crate) fn set_boxed_mut_ptr(dst: *mut *mut C, src: C::RustType) +where + C: Castable, +{ + unsafe { + *dst = to_boxed_mut_ptr(src); + } +} + +/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a const pointer +/// to an `Arc` over the rust type and sets the `dst` out pointer to the resulting const `Arc` +/// pointer. See [`to_arc_const_ptr`] for more information. +/// +/// ## Unsafety: +/// +/// `dst` must not be `NULL`. +pub(crate) fn set_arc_mut_ptr(dst: *mut *const C, src: C::RustType) +where + C: Castable, +{ + unsafe { + *dst = to_arc_const_ptr(src); + } +} + +/// Converts a mutable pointer to a [`Castable`] to an optional ref to the underlying +/// [`Castable::RustType`]. See [`cast_mut_ptr`] for more information. +/// +/// Does nothing, returning `None`, when passed `NULL`. Can only be used when the `Castable` has +/// specified a cast type equal to [`OwnershipBox`]. +pub(crate) fn try_from_mut<'a, C>(from: *mut C) -> Option<&'a mut C::RustType> +where + C: Castable, +{ + unsafe { cast_mut_ptr(from).as_mut() } +} + +/// If the provided pointer to a [`Castable`] is non-null, convert it to a mutable reference using +/// [`try_from_mut`]. Otherwise, return [`rustls_result::NullParameter`], or an appropriate default +/// (`false`, `0`, `NULL`) based on the context. See [`try_from_mut`] for more information. +macro_rules! try_mut_from_ptr { + ( $var:ident ) => { + match $crate::ffi::try_from_mut($var) { + Some(c) => c, + None => return $crate::panic::NullParameterOrDefault::value(), + } + }; +} + +pub(crate) use try_mut_from_ptr; + +/// Converts a const pointer to a [`Castable`] to an optional ref to the underlying +/// [`Castable::RustType`]. See [`cast_const_ptr`] for more information. +/// +/// Does nothing, returning `None` when passed `NULL`. Can be used with `Castable`'s that +/// specify a cast type of [`OwnershipArc`] as well as `Castable`'s that specify +/// a cast type of [`OwnershipBox`]. +pub(crate) fn try_from<'a, C, O>(from: *const C) -> Option<&'a C::RustType> +where + C: Castable, +{ + unsafe { cast_const_ptr(from).as_ref() } +} + +/// If the provided pointer to a [`Castable`] is non-null, convert it to a reference using +/// [`try_from`]. Otherwise, raise and return a `crate::error::Error::null_pointer()` error. +/// +/// See [`try_from`] for more information. +macro_rules! try_ref_from_ptr { + ( $var:ident ) => { + match $crate::ffi::try_from($var) { + Some(c) => c, + None => return $crate::error::Error::null_pointer().raise().into(), + } + }; +} + +pub(crate) use try_ref_from_ptr; + +/// If the provided pointer to a [`Castable`] is non-null, convert it to a reference to an `Arc` over +/// the underlying rust type using [`try_arc_from`]. +/// +/// Otherwise, raise and return a `crate::error::Error::null_pointer()` error. +/// In the two-argument version, the error code returned can be specified to +/// deal with inconsistent return value usages (eg. `SSL_read`). +/// +/// See [`try_arc_from`] for more information. +macro_rules! try_clone_arc { + ( $var:ident ) => { + match $crate::ffi::clone_arc($var) { + Some(c) => c, + None => return $crate::error::Error::null_pointer().raise().into(), + } + }; + ( $var:ident, $error_code:expr ) => { + match $crate::ffi::clone_arc($var) { + Some(c) => c, + None => { + $crate::error::Error::null_pointer().raise(); + return $error_code; + } + } + }; +} + +pub(crate) use try_clone_arc; + +/// Convert a mutable pointer to a [`Castable`] to an optional `Box` over the underlying +/// [`Castable::RustType`]. +/// +/// Does nothing, returning `None`, when passed `NULL`. Can only be used with `Castable`'s that +/// specify a cast type of [`OwnershipBox`]. +pub(crate) fn try_box_from(from: *mut C) -> Option> +where + C: Castable, +{ + to_box(from) +} + +/// If the provided pointer to a [`Castable`] is non-null, convert it to a reference to a `Box` +/// over the underlying rust type using [`try_box_from`]. +/// +/// Otherwise, raise and return a `crate::error::Error::null_pointer()` error. +/// +/// See [`try_box_from`] for more information. +macro_rules! try_box_from_ptr { + ( $var:ident ) => { + match $crate::ffi::try_box_from($var) { + Some(c) => c, + None => return $crate::error::Error::null_pointer().raise().into(), + } + }; +} + +pub(crate) use try_box_from_ptr; + +/// Makes a slice from a pointer and signed length. +/// +/// An error is returned if the pointer is null or the length is negative. +/// +/// In the three-argument version, the error code returned can be specified to +/// deal with inconsistent return value usages (eg. `SSL_read`). +macro_rules! try_slice_int { + ( $ptr:expr, $count:expr ) => { + if $ptr.is_null() || $count < 0 { + return $crate::error::Error::null_pointer().raise().into(); + } else { + unsafe { ::core::slice::from_raw_parts($ptr, $count as usize) } + } + }; + ( $ptr:expr, $count:expr, $error_code:expr ) => { + if $ptr.is_null() || $count < 0 { + $crate::error::Error::null_pointer().raise(); + return $error_code; + } else { + unsafe { ::core::slice::from_raw_parts($ptr, $count as usize) } + } + }; +} + +pub(crate) use try_slice_int; + +/// Makes a mutable slice from a pointer and signed length. +/// +/// An error is returned if the pointer is null or the length is negative. +/// +/// In the three-argument version, the error code returned can be specified to +/// deal with inconsistent return value usages (eg. `SSL_read`). +macro_rules! try_mut_slice_int { + ( $ptr:expr, $count:expr ) => { + if $ptr.is_null() || $count < 0 { + return $crate::error::Error::null_pointer().raise().into(); + } else { + unsafe { ::core::slice::from_raw_parts_mut($ptr, $count as usize) } + } + }; + ( $ptr:expr, $count:expr, $error_code:expr ) => { + if $ptr.is_null() || $count < 0 { + $crate::error::Error::null_pointer().raise(); + return $error_code; + } else { + unsafe { ::core::slice::from_raw_parts_mut($ptr, $count as usize) } + } + }; +} + +pub(crate) use try_mut_slice_int; + +macro_rules! try_slice { + ( $ptr:expr, $count:expr ) => { + if $ptr.is_null() { + return $crate::error::Error::null_pointer().raise().into(); + } else { + unsafe { ::core::slice::from_raw_parts($ptr, $count as usize) } + } + }; +} + +pub(crate) use try_slice; + +pub(crate) fn string_from_cstring(s: *const c_char) -> Option { + if s.is_null() { + return None; + } + + let cstr = unsafe { CStr::from_ptr(s) }; + Some(String::from_utf8_lossy(cstr.to_bytes()).to_string()) +} + +pub(crate) fn str_from_cstring(s: *const c_char) -> Option<&'static str> { + if s.is_null() { + return None; + } + + let cstr = unsafe { CStr::from_ptr(s) }; + cstr.to_str().ok() +} + +macro_rules! try_string { + ( $ptr:expr) => { + match $crate::ffi::string_from_cstring($ptr) { + Some(s) => s, + None => return $crate::error::Error::null_pointer().raise().into(), + } + }; +} + +pub(crate) use try_string; + +macro_rules! try_str { + ( $ptr:expr) => { + match $crate::ffi::str_from_cstring($ptr) { + Some(s) => s, + None => return $crate::error::Error::null_pointer().raise().into(), + } + }; +} + +pub(crate) use try_str; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs new file mode 100644 index 0000000..75c273e --- /dev/null +++ b/rustls-libssl/src/lib.rs @@ -0,0 +1,60 @@ +use std::sync::{Arc, Mutex}; + +#[allow( + // relax naming convention lints for openssl API + non_camel_case_types, + non_snake_case, + clippy::upper_case_acronyms, + // false positives on extern entrypoints + dead_code, +)] +mod entry; +mod error; +#[macro_use] +#[allow(unused_macros, dead_code, unused_imports)] +mod ffi; +#[cfg(miri)] +#[allow(non_camel_case_types, dead_code)] +mod miri; + +/// `SSL_METHOD` underlying type. +/// +/// # Lifetime +/// Functions that return SSL_METHOD, like `TLS_method()`, give static-lifetime pointers. +pub struct SslMethod { + client_versions: &'static [&'static rustls::SupportedProtocolVersion], + server_versions: &'static [&'static rustls::SupportedProtocolVersion], +} + +static TLS_CLIENT_METHOD: SslMethod = SslMethod { + client_versions: rustls::ALL_VERSIONS, + server_versions: &[], +}; +static TLS_SERVER_METHOD: SslMethod = SslMethod { + client_versions: &[], + server_versions: rustls::ALL_VERSIONS, +}; +static TLS_METHOD: SslMethod = SslMethod { + client_versions: rustls::ALL_VERSIONS, + server_versions: rustls::ALL_VERSIONS, +}; + +pub struct SslContext { + method: &'static SslMethod, +} + +impl SslContext { + fn new(method: &'static SslMethod) -> Self { + Self { method } + } +} + +struct Ssl { + ctx: Arc>, +} + +impl Ssl { + fn new(ctx: Arc>) -> Self { + Self { ctx } + } +} diff --git a/rustls-libssl/src/miri.rs b/rustls-libssl/src/miri.rs new file mode 100644 index 0000000..98d202e --- /dev/null +++ b/rustls-libssl/src/miri.rs @@ -0,0 +1,14 @@ +/// Shims for functions we call, written in rust so they are visible to miri. +use std::ffi::{c_char, c_int, CStr}; + +#[no_mangle] +pub extern "C" fn ERR_new() { + eprintln!("ERR_new()"); +} + +#[no_mangle] +pub extern "C" fn ERR_set_error(lib: c_int, reason: c_int, message: *const c_char) { + eprintln!("ERR_set_error(0x{lib:x}, 0x{reason:x}, {:?})", unsafe { + CStr::from_ptr(message) + }); +}