pax_global_header00006660000000000000000000000064144423527070014522gustar00rootroot0000000000000052 comment=0dfe5c1c01b510459130ededa59f679cc8106f96 rustls-native-certs-v-0.6.3/000077500000000000000000000000001444235270700157315ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/.github/000077500000000000000000000000001444235270700172715ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/.github/dependabot.yml000066400000000000000000000003641444235270700221240ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily open-pull-requests-limit: 10 - package-ecosystem: github-actions directory: "/" schedule: interval: weekly open-pull-requests-limit: 10 rustls-native-certs-v-0.6.3/.github/workflows/000077500000000000000000000000001444235270700213265ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/.github/workflows/rust.yml000066400000000000000000000052761444235270700230600ustar00rootroot00000000000000name: rustls permissions: contents: read on: [push, pull_request] jobs: build: name: Build+test runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # test a bunch of toolchains on ubuntu rust: - stable - beta - nightly os: [ubuntu-20.04] # but only stable on macos/windows (slower platforms) include: - os: macos-latest rust: stable - os: windows-latest rust: stable steps: - name: Checkout sources uses: actions/checkout@v3 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: cargo build (debug; default features) run: cargo build - name: cargo test (debug; default features) run: cargo test env: RUST_BACKTRACE: 1 - name: cargo test (debug; all features) run: cargo test --all-features env: RUST_BACKTRACE: 1 - name: cargo build (debug; no default features) run: cargo build --no-default-features - name: cargo test (debug; no default features; no run) run: cargo test --no-default-features --no-run - name: cargo test (release; no run) run: cargo test --release --no-run - name: run macOS integration test if: matrix.os == 'macos-latest' run: sudo bash integration-tests/macos.sh msrv: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-20.04, macos-latest, windows-latest] steps: - name: Checkout sources uses: actions/checkout@v3 with: persist-credentials: false - name: Install MSRV toolchain uses: dtolnay/rust-toolchain@master with: toolchain: "1.60" - run: cargo check --lib --all-features format: name: Format runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v3 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Check formatting run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v3 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: clippy - run: cargo clippy --all-features -- --deny warnings rustls-native-certs-v-0.6.3/.gitignore000066400000000000000000000000501444235270700177140ustar00rootroot00000000000000Cargo.lock target/ *.gcda *.gcno *.info rustls-native-certs-v-0.6.3/CONTRIBUTING.md000066400000000000000000000035371444235270700201720ustar00rootroot00000000000000# Contributing Thanks for considering helping this project. There are many ways you can help: using the library and reporting bugs, reporting usability issues, making additions and improvements to the library, documentation and finding security bugs. ## Reporting bugs Please file a github issue. Include as much information as possible. Suspected protocol bugs are easier debugged with a pcap or reproduction steps. Feel free to file github issues to get help, or ask a question. ## Code changes Some ideas and guidelines for contributions: - For large features, file an issue prior to starting work. This means everyone can see what is in progress prior to a PR. - Feel free to submit a PR even if the work is not totally finished, for feedback or to hand-over. - Prefer not to reference github issue or PR numbers in commits. - Try to keep code formatting commits separate from functional commits. - I run `cargo outdated` prior to major releases; but PRs to update specific dependencies are welcome. ## Security bugs Please report security bugs by filing a github issue, or by email to jbp@jbp.io if you want to disclose privately. I'll then: - Prepare a fix and regression tests. - Backport the fix and make a patch release for most recent release. - Submit an advisory to [rustsec/advisory-db](https://github.com/RustSec/advisory-db). - Refer to the advisory on the main README.md and release notes. ## Testing - Features involving additions to the public API should have (at least) API-level tests (see [`tests/api.rs`](tests/api.rs)). - Protocol additions should have some coverage -- consider enabling corresponding tests in the bogo suite, or writing some adhoc tests. PRs which cause test failures or a significant coverage decrease are unlikely to be accepted. ## Licensing Contributions are made under [rustls-native-certs's licenses](LICENSE). rustls-native-certs-v-0.6.3/Cargo.toml000066400000000000000000000015661444235270700176710ustar00rootroot00000000000000[package] name = "rustls-native-certs" version = "0.6.3" edition = "2021" rust-version = "1.60" license = "Apache-2.0 OR ISC OR MIT" readme = "README.md" description = "rustls-native-certs allows rustls to use the platform native certificate store" homepage = "https://github.com/ctz/rustls-native-certs" repository = "https://github.com/ctz/rustls-native-certs" categories = ["network-programming", "cryptography"] [dependencies] rustls-pemfile = "1" [dev-dependencies] ring = "0.16.5" rustls = "0.21.0" rustls-webpki = "0.100" serial_test = "2" untrusted = "0.7.0" # stick to the version ring depends on for now webpki-roots = "0.23" x509-parser = "0.15" [target.'cfg(windows)'.dependencies] schannel = "0.1.15" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] openssl-probe = "0.1.2" [target.'cfg(target_os = "macos")'.dependencies] security-framework = "2.0.0" rustls-native-certs-v-0.6.3/LICENSE000066400000000000000000000004361444235270700167410ustar00rootroot00000000000000Rustls is distributed under the following three licenses: - Apache License version 2.0. - MIT license. - ISC license. These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this software under the terms of any of these licenses, at your option. rustls-native-certs-v-0.6.3/LICENSE-APACHE000066400000000000000000000251371444235270700176650ustar00rootroot00000000000000 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. rustls-native-certs-v-0.6.3/LICENSE-ISC000066400000000000000000000014071444235270700173540ustar00rootroot00000000000000ISC License (ISC) Copyright (c) 2016, Joseph Birr-Pixton Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. rustls-native-certs-v-0.6.3/LICENSE-MIT000066400000000000000000000020721444235270700173660ustar00rootroot00000000000000Copyright (c) 2016 Joseph Birr-Pixton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rustls-native-certs-v-0.6.3/README.md000066400000000000000000000127411444235270700172150ustar00rootroot00000000000000![Logo](https://raw.githubusercontent.com/ctz/rustls/main/admin/rustls-logo-web.png) **rustls-native-certs** allows [rustls](https://github.com/ctz/rustls) to use the platform's native certificate store when operating as a TLS client. This is supported on Windows, macOS and Linux: - On all platforms, the `SSL_CERT_FILE` environment variable is checked first. If that's set, certificates are loaded from the path specified by that variable, or an error is returned if certificates cannot be loaded from the given path. If it's not set, then the platform-specific certificate source is used. - On Windows, certificates are loaded from the system certificate store. The [`schannel`](https://github.com/steffengy/schannel-rs) crate is used to access the Windows certificate store APIs. - On macOS, certificates are loaded from the keychain. The user, admin and system trust settings are merged together as documented by Apple. The [`security-framework`](https://github.com/kornelski/rust-security-framework) crate is used to access the keystore APIs. - On Linux and other UNIX-like operating systems, the [`openssl-probe`](https://github.com/alexcrichton/openssl-probe) crate is used to discover the filename of the system CA bundle. # Status rustls-native-certs is currently in development. If you'd like to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md). [![rustls](https://github.com/rustls/rustls-native-certs/actions/workflows/rust.yml/badge.svg)](https://github.com/rustls/rustls-native-certs/actions/workflows/rust.yml) [![Documentation](https://docs.rs/rustls-native-certs/badge.svg)](https://docs.rs/rustls-native-certs/) ## Release history: * 0.6.3 (2023-06-14) - Bump MSRV to 1.60. - Windows: avoid storing certificates which are currently invalid. - Implement `AsRef<[u8]>` for `Certificate`. * 0.6.2 (2022-04-14): - Update dependencies. * 0.6.1 (2021-10-25): - Allow overrides using `SSL_CERT_FILE` on all platforms. * 0.6.0 (2021-10-24): - Remove rustls dependency entirely. * 0.5.0 (2020-11-22): - Update dependencies. - Make rustls dependency optional, for use with reqwest's certificate types. Thanks to @est31. * 0.4.0 (2020-07-05): - Update dependencies. * 0.3.0 (2020-02-24): - Support wider range of UNIX platforms. - Update dependencies. * 0.2.0 (2020-01-26): - Return valid certificates even in the presence of invalid ones. This allows callers to opt-in to "best effort" behaviour. * 0.1.0 (2019-11-04): - Initial release. # API This library exposes a single function with this signature: ```rust pub fn load_native_certs() -> Result, std::io::Error> ``` On success, this returns a `Vec` loaded with a snapshot of the root certificates found on this platform. This function fails in a platform-specific way, expressed in a `std::io::Error`. This function can be expensive: on some platforms it involves loading and parsing a ~300KB disk file. It's therefore prudent to call this sparingly. # Worked example See [`examples/google.rs`](examples/google.rs). # Should I use this or `webpki-roots`? (Background: [webpki-roots](https://crates.io/crates/webpki-roots) is a crate that compiles-in Mozilla's set of root certificates.) This crate is preferable in many ways to *webpki-roots*. To sum up the pros and cons: Pros: - **This crate respects local configuration of root certificates**: both removal of roots that the user finds untrustworthy, and addition of locally-trusted roots. _The latter case is exceedingly important if your application is required to work in enterprise environments with "transparent" TLS-terminating middleboxes._ - **This crate instantaneously reflects underlying system configuration**. _Since webpki-roots compiles in root certificates, getting an update to these requires taking regular updates to this crate, plus recompilation and redeployment of the application. This is a long-winded process that may become a liability in the event of a severe misissuance._ - **This crate is compatible with developer aids** such as [mkcert](https://github.com/FiloSottile/mkcert). Cons: - **Use of the OS certificate store is not the same as relying on OS trust verification** because platform verifiers might impose additional criteria before deciding whether to trust a root ostensibly included in the OS certificate store (for example, an expiration date). - **The OS certificate store is occasionally "attacked" by [malware](https://en.wikipedia.org/wiki/Superfish)** or just [bad software](https://sennheiser.zendesk.com/hc/en-us/articles/360011888254). - **The OS update system may, in fact, be quite poor at keeping the root certificates up-to-date** if it is disabled or out-of-support. - **The quality of the `ca-certificates` package on debian-based Linux distributions is poor**. At the time of writing, this ships many certificates not included in the Mozilla set, either because they [failed an audit and were withdrawn](https://bugzilla.mozilla.org/show_bug.cgi?id=1448506) or [were removed for mississuance](https://bugzilla.mozilla.org/show_bug.cgi?id=1552374). - **You may prefer to insulate yourself against local configuration** for support or (perhaps inadvisable) security reasons. # License rustls-native-certs is distributed under the following three licenses: - Apache License version 2.0. - MIT license. - ISC license. These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this software under the terms of any of these licenses, at your option. rustls-native-certs-v-0.6.3/RELEASING.md000066400000000000000000000026651444235270700175750ustar00rootroot00000000000000# Making a rustls-native-certs release This is a checklist for steps to make before/after making a rustls release. 1. Attend to the README.md: this appears on crates.io for the release, and can't be edited after the fact. - Ensure the version has a good set of release notes. Move old release notes to OLDCHANGES.md if this is getting excessively long. - Write the version and date of the release. 2. Run `cargo update` followed by `cargo outdated`, to check if we have any dependency updates which are not already automatically taken by their semver specs. - If we do, take them if possible with separate commits (but there should've been dependabot PRs submitted for these already.) 3. Now run `cargo test --all-features` to ensure our tests continue to pass with the updated dependencies. 4. Update `Cargo.toml` to set the correct version. 5. Make a commit with the above changes, something like 'Prepare $VERSION'. This should not contain functional changes: just versions numbers, and markdown changes. 6. Do a dry run: check `cargo publish --dry-run` 7. Push the above commit. Wait for CI to confirm it as green. - Any red _should_ naturally block the release. - If rustc nightly is broken, this _may_ be acceptable if the reason is understood and does not point to a defect. 8. Tag the released version: `git tag -m '0.20.0' v/0.20.0` 9. Push the tag: `git push --tags` 10. Do the release: `cargo publish`. rustls-native-certs-v-0.6.3/examples/000077500000000000000000000000001444235270700175475ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/examples/google.rs000066400000000000000000000026231444235270700213740ustar00rootroot00000000000000use std::convert::TryInto; use std::sync::Arc; use std::io::{stdout, Read, Write}; use std::net::TcpStream; fn main() { let mut roots = rustls::RootCertStore::empty(); for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { roots .add(&rustls::Certificate(cert.0)) .unwrap(); } let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); let mut conn = rustls::ClientConnection::new(Arc::new(config), "google.com".try_into().unwrap()).unwrap(); let mut sock = TcpStream::connect("google.com:443").expect("cannot connect"); let mut tls = rustls::Stream::new(&mut conn, &mut sock); tls.write_all( concat!( "GET / HTTP/1.1\r\n", "Host: google.com\r\n", "Connection: close\r\n", "Accept-Encoding: identity\r\n", "\r\n" ) .as_bytes(), ) .expect("write failed"); let ciphersuite = tls .conn .negotiated_cipher_suite() .expect("tls handshake failed"); writeln!( &mut std::io::stderr(), "Current ciphersuite: {:?}", ciphersuite.suite() ) .unwrap(); let mut plaintext = Vec::new(); tls.read_to_end(&mut plaintext).unwrap(); stdout().write_all(&plaintext).unwrap(); } rustls-native-certs-v-0.6.3/examples/print-trust-anchors.rs000066400000000000000000000006601444235270700240650ustar00rootroot00000000000000//! Print the Subject of all extracted trust anchors. use std::error::Error; use x509_parser::prelude::*; fn main() -> Result<(), Box> { for cert in rustls_native_certs::load_native_certs()? { match parse_x509_certificate(&cert.0) { Ok((_, cert)) => println!("{}", cert.tbs_certificate.subject), Err(e) => eprintln!("error parsing certificate: {}", e), }; } Ok(()) } rustls-native-certs-v-0.6.3/integration-tests/000077500000000000000000000000001444235270700214145ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/integration-tests/macos.sh000066400000000000000000000015021444235270700230500ustar00rootroot00000000000000#!/bin/bash set -ex ANY_CA_PEM=integration-tests/one-existing-ca.pem ANY_CA_SUBJECT="OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign" reset() { security remove-trusted-cert -d $ANY_CA_PEM || true list | grep "$ANY_CA_SUBJECT" } list() { cargo test util_list_certs -- --nocapture 2>/dev/null } assert_missing() { set +e list | grep "$1" ret=$? set -e test $ret -eq 1 } assert_exists() { list | grep "$1" > /dev/null } test_distrust_existing_root() { assert_exists "$ANY_CA_SUBJECT" security add-trusted-cert -d -r deny $ANY_CA_PEM assert_missing "$ANY_CA_SUBJECT" reset } # https://developer.apple.com/forums/thread/671582?answerId=693632022#693632022 security authorizationdb write com.apple.trust-settings.admin allow reset test_distrust_existing_root printf "\n*** All tests passed ***\n" rustls-native-certs-v-0.6.3/integration-tests/one-existing-ca.pem000066400000000000000000000023151444235270700251120ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK 6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH WD9f -----END CERTIFICATE----- rustls-native-certs-v-0.6.3/rustfmt.toml000066400000000000000000000000171444235270700203300ustar00rootroot00000000000000chain_width=40 rustls-native-certs-v-0.6.3/src/000077500000000000000000000000001444235270700165205ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/src/lib.rs000066400000000000000000000057521444235270700176450ustar00rootroot00000000000000//! rustls-native-certs allows rustls to use the platform's native certificate //! store when operating as a TLS client. //! //! It provides a single function [`load_native_certs()`], which returns a //! collection of certificates found by reading the platform-native //! certificate store. //! //! If the SSL_CERT_FILE environment variable is set, certificates (in PEM //! format) are read from that file instead. //! //! [`Certificate`] here is just a marker newtype that denotes a DER-encoded //! X.509 certificate encoded as a `Vec`. //! //! If you want to load these certificates into a `rustls::RootCertStore`, //! you'll likely want to do something like this: //! //! ```no_run //! let mut roots = rustls::RootCertStore::empty(); //! for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { //! roots //! .add(&rustls::Certificate(cert.0)) //! .unwrap(); //! } //! ``` #[cfg(all(unix, not(target_os = "macos")))] mod unix; #[cfg(all(unix, not(target_os = "macos")))] use unix as platform; #[cfg(windows)] mod windows; #[cfg(windows)] use windows as platform; #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] use macos as platform; use std::env; use std::fs::File; use std::io::BufReader; use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; /// Load root certificates found in the platform's native certificate store. /// /// If the SSL_CERT_FILE environment variable is set, certificates (in PEM /// format) are read from that file instead. /// /// This function fails in a platform-specific way, expressed in a `std::io::Error`. /// /// This function can be expensive: on some platforms it involves loading /// and parsing a ~300KB disk file. It's therefore prudent to call /// this sparingly. pub fn load_native_certs() -> Result, Error> { load_certs_from_env().unwrap_or_else(platform::load_native_certs) } /// A newtype representing a single DER-encoded X.509 certificate encoded as a `Vec`. pub struct Certificate(pub Vec); impl AsRef<[u8]> for Certificate { fn as_ref(&self) -> &[u8] { &self.0 } } const ENV_CERT_FILE: &str = "SSL_CERT_FILE"; /// Returns None if SSL_CERT_FILE is not defined in the current environment. /// /// If it is defined, it is always used, so it must be a path to a real /// file from which certificates can be loaded successfully. fn load_certs_from_env() -> Option, Error>> { let cert_var_path = PathBuf::from(env::var_os(ENV_CERT_FILE)?); Some(load_pem_certs(&cert_var_path)) } fn load_pem_certs(path: &Path) -> Result, Error> { let f = File::open(path)?; let mut f = BufReader::new(f); match rustls_pemfile::certs(&mut f) { Ok(contents) => Ok(contents .into_iter() .map(Certificate) .collect()), Err(err) => Err(Error::new( ErrorKind::InvalidData, format!("Could not load PEM file {path:?}: {err}"), )), } } rustls-native-certs-v-0.6.3/src/macos.rs000066400000000000000000000037571444235270700202040ustar00rootroot00000000000000use crate::Certificate; use security_framework::trust_settings::{Domain, TrustSettings, TrustSettingsForCertificate}; use std::collections::HashMap; use std::io::{Error, ErrorKind}; pub fn load_native_certs() -> Result, Error> { // The various domains are designed to interact like this: // // "Per-user Trust Settings override locally administered // Trust Settings, which in turn override the System Trust // Settings." // // So we collect the certificates in this order; as a map of // their DER encoding to what we'll do with them. We don't // overwrite existing elements, which mean User settings // trump Admin trump System, as desired. let mut all_certs = HashMap::new(); for domain in &[Domain::User, Domain::Admin, Domain::System] { let ts = TrustSettings::new(*domain); let iter = ts .iter() .map_err(|err| Error::new(ErrorKind::Other, err))?; for cert in iter { let der = cert.to_der(); // If there are no specific trust settings, the default // is to trust the certificate as a root cert. Weird API but OK. // The docs say: // // "Note that an empty Trust Settings array means "always trust this cert, // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot". let trusted = ts .tls_trust_settings_for_certificate(&cert) .map_err(|err| Error::new(ErrorKind::Other, err))? .unwrap_or(TrustSettingsForCertificate::TrustRoot); all_certs.entry(der).or_insert(trusted); } } let mut certs = Vec::new(); // Now we have all the certificates and an idea of whether // to use them. for (der, trusted) in all_certs.drain() { use TrustSettingsForCertificate::*; if let TrustRoot | TrustAsRoot = trusted { certs.push(Certificate(der)); } } Ok(certs) } rustls-native-certs-v-0.6.3/src/unix.rs000066400000000000000000000005031444235270700200470ustar00rootroot00000000000000use crate::load_pem_certs; use crate::Certificate; use std::io::Error; pub fn load_native_certs() -> Result, Error> { let likely_locations = openssl_probe::probe(); match likely_locations.cert_file { Some(cert_file) => load_pem_certs(&cert_file), None => Ok(Vec::new()), } } rustls-native-certs-v-0.6.3/src/windows.rs000066400000000000000000000014401444235270700205570ustar00rootroot00000000000000use crate::Certificate; use std::io::Error; static PKIX_SERVER_AUTH: &str = "1.3.6.1.5.5.7.3.1"; fn usable_for_rustls(uses: schannel::cert_context::ValidUses) -> bool { match uses { schannel::cert_context::ValidUses::All => true, schannel::cert_context::ValidUses::Oids(strs) => strs .iter() .any(|x| x == PKIX_SERVER_AUTH), } } pub fn load_native_certs() -> Result, Error> { let mut certs = Vec::new(); let current_user_store = schannel::cert_store::CertStore::open_current_user("ROOT")?; for cert in current_user_store.certs() { if usable_for_rustls(cert.valid_uses().unwrap()) && cert.is_time_valid().unwrap() { certs.push(Certificate(cert.to_der().to_vec())); } } Ok(certs) } rustls-native-certs-v-0.6.3/tests/000077500000000000000000000000001444235270700170735ustar00rootroot00000000000000rustls-native-certs-v-0.6.3/tests/badssl-com-chain.pem000066400000000000000000000023611444235270700227040ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDeTCCAmGgAwIBAgIJAMnA8BB8xT6wMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0y MTEwMTEyMDAzNTRaFw0yMzEwMTEyMDAzNTRaMGIxCzAJBgNVBAYTAlVTMRMwEQYD VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2 PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI hvcNAQELBQADggEBAC4DensZ5tCTeCNJbHABYPwwqLUFOMITKOOgF3t8EqOan0CH ST1NNi4jPslWrVhQ4Y3UbAhRBdqXl5N/NFfMzDosPpOjFgtifh8Z2s3w8vdlEZzf A4mYTC8APgdpWyNgMsp8cdXQF7QOfdnqOfdnY+pfc8a8joObR7HEaeVxhJs+XL4E CLByw5FR+svkYgCbQGWIgrM1cRpmXemt6Gf/XgFNP2PdubxqDEcnWlTMk8FCBVb1 nVDSiPjYShwnWsOOshshCRCAiIBPCKPX0QwKDComQlRrgMIvddaSzFFTKPoNZjC+ CUspSNnL7V9IIHvqKlRSmu+zIpm2VJCp1xLulk8= -----END CERTIFICATE----- rustls-native-certs-v-0.6.3/tests/compare_mozilla.rs000066400000000000000000000125471444235270700226270ustar00rootroot00000000000000//! This test attempts to verify that the set of 'native' //! certificates produced by this crate is roughly similar //! to the set of certificates in the mozilla root program //! as expressed by the `webpki-roots` crate. //! //! This is, obviously, quite a heuristic test. use std::collections::HashMap; use ring::io::der; use webpki::TrustAnchor; fn stringify_x500name(subject: &[u8]) -> String { let mut parts = vec![]; let mut reader = untrusted::Reader::new(subject.into()); while !reader.at_end() { let (tag, contents) = der::read_tag_and_get_value(&mut reader).unwrap(); assert!(tag == 0x31); // sequence, constructed, context=1 let mut inner = untrusted::Reader::new(contents); let pair = der::expect_tag_and_get_value(&mut inner, der::Tag::Sequence).unwrap(); let mut pair = untrusted::Reader::new(pair); let oid = der::expect_tag_and_get_value(&mut pair, der::Tag::OID).unwrap(); let (valuety, value) = der::read_tag_and_get_value(&mut pair).unwrap(); let name = match oid.as_slice_less_safe() { [0x55, 0x04, 0x03] => "CN", [0x55, 0x04, 0x05] => "serialNumber", [0x55, 0x04, 0x06] => "C", [0x55, 0x04, 0x07] => "L", [0x55, 0x04, 0x08] => "ST", [0x55, 0x04, 0x09] => "STREET", [0x55, 0x04, 0x0a] => "O", [0x55, 0x04, 0x0b] => "OU", [0x55, 0x04, 0x11] => "postalCode", [0x55, 0x04, 0x61] => "organizationIdentifier", [0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19] => "domainComponent", [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01] => "emailAddress", _ => panic!("unhandled x500 attr {:?}", oid), }; let str_value = match valuety { // PrintableString, UTF8String, TeletexString or IA5String 0x0c | 0x13 | 0x14 | 0x16 => std::str::from_utf8(value.as_slice_less_safe()).unwrap(), _ => panic!("unhandled x500 value type {:?}", valuety), }; parts.push(format!("{}={}", name, str_value)); } parts.join(", ") } fn to_map<'a>( anchors: &'a [webpki::TrustAnchor<'a>], ) -> HashMap, &'a webpki::TrustAnchor<'a>> { let mut r = HashMap::new(); for anchor in anchors { r.insert(anchor.spki.to_vec(), anchor); } r } #[test] fn test_does_not_have_many_roots_unknown_by_mozilla() { let native = rustls_native_certs::load_native_certs().unwrap(); let mozilla = to_map(webpki_roots::TLS_SERVER_ROOTS.0); let mut missing_in_moz_roots = 0; for cert in &native { let cert = TrustAnchor::try_from_cert_der(&cert.0).unwrap(); if let Some(moz) = mozilla.get(cert.spki) { assert_eq!(cert.subject, moz.subject, "subjects differ for public key"); } else { println!( "Native anchor {:?} is missing from mozilla set", stringify_x500name(cert.subject) ); missing_in_moz_roots += 1; } } #[cfg(windows)] let threshold = 2.0; // no more than 160% extra roots; windows CI vm has lots of extra roots #[cfg(target_os = "macos")] let threshold = 0.6; // macOS has a bunch of extra roots, too #[cfg(not(any(windows, target_os = "macos")))] let threshold = 0.5; // no more than 50% extra roots let diff = (missing_in_moz_roots as f64) / (mozilla.len() as f64); println!("mozilla: {:?}", mozilla.len()); println!("native: {:?}", native.len()); println!( "{:?} anchors present in native set but not mozilla ({}%)", missing_in_moz_roots, diff * 100. ); assert!(diff < threshold, "too many unknown roots"); } #[test] fn test_contains_most_roots_known_by_mozilla() { let native = rustls_native_certs::load_native_certs().unwrap(); let mut native_map = HashMap::new(); for anchor in &native { let cert = TrustAnchor::try_from_cert_der(&anchor.0).unwrap(); native_map.insert(cert.spki.to_vec(), anchor); } let mut missing_in_native_roots = 0; let mozilla = webpki_roots::TLS_SERVER_ROOTS.0; for cert in mozilla { if native_map.get(cert.spki).is_none() { println!( "Mozilla anchor {:?} is missing from native set", stringify_x500name(cert.subject) ); missing_in_native_roots += 1; } } #[cfg(windows)] let threshold = 0.95; // no more than 95% extra roots; windows misses *many* roots #[cfg(target_os = "macos")] let threshold = 0.6; // no more than 60% extra roots; macOS has a bunch of extra roots, too #[cfg(not(any(windows, target_os = "macos")))] let threshold = 0.5; // no more than 50% extra roots let diff = (missing_in_native_roots as f64) / (mozilla.len() as f64); println!("mozilla: {:?}", mozilla.len()); println!("native: {:?}", native.len()); println!( "{:?} anchors present in mozilla set but not native ({}%)", missing_in_native_roots, diff * 100. ); assert!(diff < threshold, "too many missing roots"); } #[test] fn util_list_certs() { let native = rustls_native_certs::load_native_certs().unwrap(); for (i, cert) in native.iter().enumerate() { let cert = TrustAnchor::try_from_cert_der(&cert.0).unwrap(); println!("cert[{}] = {}", i, stringify_x500name(cert.subject)); } } rustls-native-certs-v-0.6.3/tests/smoketests.rs000066400000000000000000000045201444235270700216430ustar00rootroot00000000000000use std::convert::TryInto; use std::sync::Arc; use std::panic; use std::env; use std::io::{Read, Write}; use std::net::TcpStream; use std::path::PathBuf; // #[serial] is used on all these tests to run them sequentially. If they're run in parallel, // the global env var configuration in the env var test interferes with the others. use serial_test::serial; fn check_site(domain: &str) { let mut roots = rustls::RootCertStore::empty(); for cert in rustls_native_certs::load_native_certs().unwrap() { roots .add(&rustls::Certificate(cert.0)) .unwrap(); } let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); let mut conn = rustls::ClientConnection::new(Arc::new(config), domain.try_into().unwrap()).unwrap(); let mut sock = TcpStream::connect(format!("{}:443", domain)).unwrap(); let mut tls = rustls::Stream::new(&mut conn, &mut sock); tls.write_all( format!( "GET / HTTP/1.1\r\n\ Host: {}\r\n\ Connection: close\r\n\ Accept-Encoding: identity\r\n\ \r\n", domain ) .as_bytes(), ) .unwrap(); let mut plaintext = [0u8; 1024]; let len = tls.read(&mut plaintext).unwrap(); assert!(plaintext[..len].starts_with(b"HTTP/1.1 ")); // or whatever } #[test] #[serial] fn google() { check_site("google.com"); } #[test] #[serial] fn amazon() { check_site("amazon.com"); } #[test] #[serial] fn facebook() { check_site("facebook.com"); } #[test] #[serial] fn netflix() { check_site("netflix.com"); } #[test] #[serial] fn ebay() { check_site("ebay.com"); } #[test] #[serial] fn apple() { check_site("apple.com"); } #[test] #[serial] fn badssl_with_env() { let result = panic::catch_unwind(|| check_site("self-signed.badssl.com")); // Self-signed certs should never be trusted by default: assert!(result.is_err()); // But they should be trusted if SSL_CERT_FILE is set: env::set_var( "SSL_CERT_FILE", // The CA cert, downloaded directly from the site itself: PathBuf::from("./tests/badssl-com-chain.pem"), ); check_site("self-signed.badssl.com"); env::remove_var("SSL_CERT_FILE"); }