native-tls-0.2.3/.circleci/config.yml010064400017500001750000000034451346075503600156650ustar0000000000000000version: 2.1 jobs: linux: parameters: image: type: string docker: - image: rust:<< parameters.image >> environment: RUST_BACKTRACE: 1 RUSTFLAGS: -D warnings steps: - checkout - restore_cache: key: registry - run: cargo generate-lockfile - save_cache: key: registry-{{ .BuildNum }} paths: - /usr/local/cargo/registry/index - restore_cache: key: deps-<< parameters.image >>-{{ checksum "Cargo.lock" }} - run: cargo test - run: rustdoc --test README.md -L target/debug/deps -L target/debug - save_cache: key: deps-<< parameters.image >>-{{ checksum "Cargo.lock" }} paths: - /usr/local/cargo/registry/cache - target macos: parameters: version: type: string macos: xcode: "9.0" environment: RUST_BACKTRACE: 1 RUSTFLAGS: -D warnings steps: - checkout - run: curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain << parameters.version >> - run: ln -s ~/.cargo/bin/* /usr/local/bin - restore_cache: key: macos-registry - run: cargo generate-lockfile - save_cache: key: macos-registry-{{ .BuildNum }} paths: - ~/.cargo/registry/index - restore_cache: key: macos-deps-<< parameters.version >> - run: cargo test - save_cache: key: macos-deps-<< parameters.version >> paths: - ~/.cargo/registry/cache - target workflows: test: jobs: - linux: name: openssl-1.1.0 image: 1.26.2-stretch - linux: name: openssl-1.0.2 image: 1.26.2-jessie - macos: version: 1.26.2 native-tls-0.2.3/.gitignore010064400017500001750000000000461336312040100140040ustar0000000000000000target Cargo.lock .idea *.iml .vscode native-tls-0.2.3/CHANGELOG.md010064400017500001750000000052551336337343000136500ustar0000000000000000# Change Log ## [Unreleased] ## [v0.2.2] ### Fixed * Failure to load a root certificate on Android now logs a message rather than producing an error. * Fixed ordering of the certificate chain in the OpenSSL backend. ## [v0.2.1] ### Added * The `vendored` Cargo feature will cause the crate to compile and statically link to a vendored copy of OpenSSL on platforms that use that backend. ## [v0.2.0] ### Added * The `openssl_probe` crate is now used with the OpenSSL backend so that trusted root certificates will automatically be detected when statically linking to OpenSSL. * Root certificates are now automatically loaded from the Android trust root. * Added `Certificate::to_der` to serialize an X509 certificate to DER. * Added `TlsConnectorBuilder::danger_accept_invalid_certs` to disable certificate verification. * Added `TlsAcceptor::new` and `TlsConnector::new` to easily create an acceptor/connector with default settings. * Added `TlsStream::peer_certificate` to obtain the peer's leaf certificate. * Added `TlsStream::tls_server_end_point` to retrieve RFC 5929 tls-server-end-point channel binding data. ### Changed * Upgraded to `openssl` 0.10 and `security-framework` 0.2. * `Pkcs12` has been renamed to `Identity`, and `Pkcs12::from_der` has been renamed to `Identity::from_pkcs12`. * `HandshakeError::Interrupted` has been renamed to `HandshakeError::WouldBlock`. * `TlsConnectorBuilder` and `TlsAcceptorBuilder` are now "traditional"-style builders. Their methods are now infallible and return `&mut Self` to allow them to be chained together. * `supported_protocols` has been replaced by `min_protocol_version` and `max_protocol_version` on `TlsConnectorBuilder` and `TlsAcceptorBuilder`. * SNI and hostname verification are now configured separately via `TlsConnectorBuilder::use_sni` and `TlsConnectorBuilder::danger_accept_invalid_hostnames`. They replace the `TlsConnector::danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication` method, which has been removed. ### Removed * The backend-specific extension traits have been removed. We want to avoid exposing the specific version of the backend library in the public API to provide more flexibility. ## Older Look at the [release tags] for information about older releases. [Unreleased]: https://github.com/sfackler/rust-native-tls/compare/v0.2.2...master [v0.2.2]: https://github.com/sfackler/rust-native-tls/compare/v0.2.1...v0.2.2 [v0.2.1]: https://github.com/sfackler/rust-native-tls/compare/v0.2.0...v0.2.1 [v0.2.0]: https://github.com/sfackler/rust-native-tls/compare/v0.1.5...v0.2.0 [release tags]: https://github.com/sfackler/rust-native-tls/releases native-tls-0.2.3/Cargo.toml.orig010064400017500001750000000014261346075504200147230ustar0000000000000000[package] name = "native-tls" version = "0.2.3" authors = ["Steven Fackler "] license = "MIT/Apache-2.0" description = "A wrapper over a platform's native TLS implementation" repository = "https://github.com/sfackler/rust-native-tls" readme = "README.md" [features] vendored = ["openssl/vendored"] [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] security-framework = "0.3.1" security-framework-sys = "0.3.1" lazy_static = "1.0" libc = "0.2" tempfile = "3.0" [target.'cfg(target_os = "windows")'.dependencies] schannel = "0.1.13" [target.'cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))'.dependencies] log = "0.4.5" openssl = "0.10.15" openssl-sys = "0.9.30" openssl-probe = "0.1" [dev-dependencies] hex = "0.3" native-tls-0.2.3/Cargo.toml0000644000000037760000000000000111760ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "native-tls" version = "0.2.3" authors = ["Steven Fackler "] description = "A wrapper over a platform's native TLS implementation" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/sfackler/rust-native-tls" [dev-dependencies.hex] version = "0.3" [features] vendored = ["openssl/vendored"] [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.lazy_static] version = "1.0" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.libc] version = "0.2" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.security-framework] version = "0.3.1" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.security-framework-sys] version = "0.3.1" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.tempfile] version = "3.0" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.log] version = "0.4.5" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl] version = "0.10.15" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl-probe] version = "0.1" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl-sys] version = "0.9.30" [target."cfg(target_os = \"windows\")".dependencies.schannel] version = "0.1.13" native-tls-0.2.3/Cargo.toml.orig0000644000000037770000000000000121360ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "native-tls" version = "0.2.3" authors = ["Steven Fackler "] description = "A wrapper over a platform's native TLS implementation" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/sfackler/rust-native-tls" [dev-dependencies.hex] version = "0.3" [features] vendored = ["openssl/vendored"] [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.lazy_static] version = "1.0" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.libc] version = "0.2" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.security-framework] version = "0.3.1" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.security-framework-sys] version = "0.3.1" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.tempfile] version = "3.0" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.log] version = "0.4.5" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl] version = "0.10.15" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl-probe] version = "0.1" [target."cfg(not(any(target_os = \"windows\", target_os = \"macos\", target_os = \"ios\")))".dependencies.openssl-sys] version = "0.9.30" [target."cfg(target_os = \"windows\")".dependencies.schannel] version = "0.1.13" native-tls-0.2.3/LICENSE-APACHE010064400017500001750000000261361336312040100137500ustar0000000000000000 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. native-tls-0.2.3/LICENSE-MIT010064400017500001750000000020621336312040100134500ustar0000000000000000Copyright (c) 2016 The rust-native-tls Developers 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. native-tls-0.2.3/README.md010064400017500001750000000052541336312040100133010ustar0000000000000000# rust-native-tls [![CircleCI](https://circleci.com/gh/sfackler/rust-native-tls.svg?style=shield)](https://circleci.com/gh/sfackler/rust-native-tls) [![Build Status](https://travis-ci.org/sfackler/rust-native-tls.svg?branch=master)](https://travis-ci.org/sfackler/rust-native-tls) [Documentation](https://docs.rs/native-tls) An abstraction over platform-specific TLS implementations. Specifically, this crate uses SChannel on Windows (via the [`schannel`] crate), Secure Transport on OSX (via the [`security-framework`] crate), and OpenSSL (via the [`openssl`] crate) on all other platforms. [`schannel`]: https://crates.io/crates/schannel [`security-framework`]: https://crates.io/crates/security-framework [`openssl`]: https://crates.io/crates/openssl ## Installation ```toml # Cargo.toml [dependencies] native-tls = "0.2" ``` ## Usage An example client looks like: ```rust extern crate native_tls; use native_tls::TlsConnector; use std::io::{Read, Write}; use std::net::TcpStream; fn main() { let connector = TlsConnector::new().unwrap(); let stream = TcpStream::connect("google.com:443").unwrap(); let mut stream = connector.connect("google.com", stream).unwrap(); stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); let mut res = vec![]; stream.read_to_end(&mut res).unwrap(); println!("{}", String::from_utf8_lossy(&res)); } ``` To accept connections as a server from remote clients: ```rust,no_run extern crate native_tls; use native_tls::{Identity, TlsAcceptor, TlsStream}; use std::fs::File; use std::io::{Read}; use std::net::{TcpListener, TcpStream}; use std::sync::Arc; use std::thread; fn main() { let mut file = File::open("identity.pfx").unwrap(); let mut identity = vec![]; file.read_to_end(&mut identity).unwrap(); let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap(); let acceptor = TlsAcceptor::new(identity).unwrap(); let acceptor = Arc::new(acceptor); let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); fn handle_client(stream: TlsStream) { // ... } for stream in listener.incoming() { match stream { Ok(stream) => { let acceptor = acceptor.clone(); thread::spawn(move || { let stream = acceptor.accept(stream).unwrap(); handle_client(stream); }); } Err(e) => { /* connection failed */ } } } } ``` # License `rust-native-tls` is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses. See LICENSE-APACHE, and LICENSE-MIT for details. native-tls-0.2.3/appveyor.yml010064400017500001750000000006161346075503600144270ustar0000000000000000environment: RUST_VERSION: 1.26.2 TARGET: x86_64-pc-windows-msvc install: - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:RUST_VERSION}-${env:TARGET}.exe" - rust-%RUST_VERSION%-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files\Rust" - SET PATH=%PATH%;C:\Program Files\Rust\bin - rustc -V - cargo -V build: false test_script: - cargo test native-tls-0.2.3/build.rs010064400017500001750000000010331336312040100134560ustar0000000000000000use std::env; fn main() { if let Ok(version) = env::var("DEP_OPENSSL_VERSION_NUMBER") { let version = u64::from_str_radix(&version, 16).unwrap(); if version >= 0x1_01_00_00_0 { println!("cargo:rustc-cfg=have_min_max_version"); } } if let Ok(version) = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") { let version = u64::from_str_radix(&version, 16).unwrap(); if version >= 0x2_06_01_00_0 { println!("cargo:rustc-cfg=have_min_max_version"); } } } native-tls-0.2.3/examples/google-connect.rs010064400017500001750000000007511336312040100171060ustar0000000000000000extern crate native_tls; use native_tls::TlsConnector; use std::io::{Read, Write}; use std::net::TcpStream; fn main() { let connector = TlsConnector::new().unwrap(); let stream = TcpStream::connect("google.com:443").unwrap(); let mut stream = connector.connect("google.com", stream).unwrap(); stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); let mut res = vec![]; stream.read_to_end(&mut res).unwrap(); println!("{}", String::from_utf8_lossy(&res)); } native-tls-0.2.3/examples/simple-server.rs010064400017500001750000000020101336312040100167660ustar0000000000000000extern crate native_tls; use native_tls::{Identity, TlsAcceptor, TlsStream}; use std::fs::File; use std::io::Read; use std::net::{TcpListener, TcpStream}; use std::sync::Arc; use std::thread; fn main() { let mut file = File::open("identity.pfx").unwrap(); let mut pkcs12 = vec![]; file.read_to_end(&mut pkcs12).unwrap(); let pkcs12 = Identity::from_pkcs12(&pkcs12, "hunter2").unwrap(); let acceptor = TlsAcceptor::new(pkcs12).unwrap(); let acceptor = Arc::new(acceptor); let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); fn handle_client(_stream: TlsStream) { // ... } for stream in listener.incoming() { match stream { Ok(stream) => { let acceptor = acceptor.clone(); thread::spawn(move || { let stream = acceptor.accept(stream).unwrap(); handle_client(stream); }); } Err(_e) => { /* connection failed */ } } } } native-tls-0.2.3/src/imp/openssl.rs010064400017500001750000000270001343664654700154510ustar0000000000000000extern crate openssl; extern crate openssl_probe; use self::openssl::error::ErrorStack; use self::openssl::hash::MessageDigest; use self::openssl::nid::Nid; use self::openssl::pkcs12::{ParsedPkcs12, Pkcs12}; use self::openssl::ssl::{ self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod, SslVerifyMode, }; use self::openssl::x509::{X509, X509VerifyResult}; use std::error; use std::fmt; use std::io; use std::sync::{Once, ONCE_INIT}; use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder}; #[cfg(have_min_max_version)] fn supported_protocols( min: Option, max: Option, ctx: &mut SslContextBuilder, ) -> Result<(), ErrorStack> { use self::openssl::ssl::SslVersion; fn cvt(p: Protocol) -> SslVersion { match p { Protocol::Sslv3 => SslVersion::SSL3, Protocol::Tlsv10 => SslVersion::TLS1, Protocol::Tlsv11 => SslVersion::TLS1_1, Protocol::Tlsv12 => SslVersion::TLS1_2, Protocol::__NonExhaustive => unreachable!(), } } ctx.set_min_proto_version(min.map(cvt))?; ctx.set_max_proto_version(max.map(cvt))?; Ok(()) } #[cfg(not(have_min_max_version))] fn supported_protocols( min: Option, max: Option, ctx: &mut SslContextBuilder, ) -> Result<(), ErrorStack> { use self::openssl::ssl::SslOptions; let no_ssl_mask = SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2; ctx.clear_options(no_ssl_mask); let mut options = SslOptions::empty(); options |= match min { None => SslOptions::empty(), Some(Protocol::Sslv3) => SslOptions::NO_SSLV2, Some(Protocol::Tlsv10) => SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3, Some(Protocol::Tlsv11) => { SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_TLSV1 } Some(Protocol::Tlsv12) => { SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1 } Some(Protocol::__NonExhaustive) => unreachable!(), }; options |= match max { None | Some(Protocol::Tlsv12) => SslOptions::empty(), Some(Protocol::Tlsv11) => SslOptions::NO_TLSV1_2, Some(Protocol::Tlsv10) => SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2, Some(Protocol::Sslv3) => { SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2 } Some(Protocol::__NonExhaustive) => unreachable!(), }; ctx.set_options(options); Ok(()) } fn init_trust() { static ONCE: Once = ONCE_INIT; ONCE.call_once(|| openssl_probe::init_ssl_cert_env_vars()); } #[cfg(target_os = "android")] fn load_android_root_certs(connector: &mut SslContextBuilder) -> Result<(), Error> { use std::fs; if let Ok(dir) = fs::read_dir("/system/etc/security/cacerts") { let certs = dir .filter_map(|r| r.ok()) .filter_map(|e| fs::read(e.path()).ok()) .filter_map(|b| X509::from_pem(&b).ok()); for cert in certs { if let Err(err) = connector.cert_store_mut().add_cert(cert) { debug!("load_android_root_certs error: {:?}", err); } } } Ok(()) } #[derive(Debug)] pub enum Error { Normal(ErrorStack), Ssl(ssl::Error, X509VerifyResult), } impl error::Error for Error { fn description(&self) -> &str { match *self { Error::Normal(ref e) => error::Error::description(e), Error::Ssl(ref e, _) => error::Error::description(e), } } fn cause(&self) -> Option<&error::Error> { match *self { Error::Normal(ref e) => error::Error::cause(e), Error::Ssl(ref e, _) => error::Error::cause(e), } } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Error::Normal(ref e) => fmt::Display::fmt(e, fmt), Error::Ssl(ref e, X509VerifyResult::OK) => fmt::Display::fmt(e, fmt), Error::Ssl(ref e, v) => write!(fmt, "{} ({})", e, v), } } } impl From for Error { fn from(err: ErrorStack) -> Error { Error::Normal(err) } } pub struct Identity(ParsedPkcs12); impl Identity { pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result { let pkcs12 = Pkcs12::from_der(buf)?; let parsed = pkcs12.parse(pass)?; Ok(Identity(parsed)) } } #[derive(Clone)] pub struct Certificate(X509); impl Certificate { pub fn from_der(buf: &[u8]) -> Result { let cert = X509::from_der(buf)?; Ok(Certificate(cert)) } pub fn from_pem(buf: &[u8]) -> Result { let cert = X509::from_pem(buf)?; Ok(Certificate(cert)) } pub fn to_der(&self) -> Result, Error> { let der = self.0.to_der()?; Ok(der) } } pub struct MidHandshakeTlsStream(MidHandshakeSslStream); impl fmt::Debug for MidHandshakeTlsStream where S: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl MidHandshakeTlsStream { pub fn get_ref(&self) -> &S { self.0.get_ref() } pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } } impl MidHandshakeTlsStream where S: io::Read + io::Write, { pub fn handshake(self) -> Result, HandshakeError> { match self.0.handshake() { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } pub enum HandshakeError { Failure(Error), WouldBlock(MidHandshakeTlsStream), } impl From> for HandshakeError { fn from(e: ssl::HandshakeError) -> HandshakeError { match e { ssl::HandshakeError::SetupFailure(e) => HandshakeError::Failure(e.into()), ssl::HandshakeError::Failure(e) => { let v = e.ssl().verify_result(); HandshakeError::Failure(Error::Ssl(e.into_error(), v)) } ssl::HandshakeError::WouldBlock(s) => { HandshakeError::WouldBlock(MidHandshakeTlsStream(s)) } } } } impl From for HandshakeError { fn from(e: ErrorStack) -> HandshakeError { HandshakeError::Failure(e.into()) } } #[derive(Clone)] pub struct TlsConnector { connector: SslConnector, use_sni: bool, accept_invalid_hostnames: bool, accept_invalid_certs: bool, } impl TlsConnector { pub fn new(builder: &TlsConnectorBuilder) -> Result { init_trust(); let mut connector = SslConnector::builder(SslMethod::tls())?; if let Some(ref identity) = builder.identity { connector.set_certificate(&(identity.0).0.cert)?; connector.set_private_key(&(identity.0).0.pkey)?; if let Some(ref chain) = (identity.0).0.chain { for cert in chain.iter().rev() { connector.add_extra_chain_cert(cert.to_owned())?; } } } supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?; for cert in &builder.root_certificates { if let Err(err) = connector.cert_store_mut().add_cert((cert.0).0.clone()) { debug!("add_cert error: {:?}", err); } } #[cfg(target_os = "android")] load_android_root_certs(&mut connector)?; Ok(TlsConnector { connector: connector.build(), use_sni: builder.use_sni, accept_invalid_hostnames: builder.accept_invalid_hostnames, accept_invalid_certs: builder.accept_invalid_certs, }) } pub fn connect(&self, domain: &str, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let mut ssl = self .connector .configure()? .use_server_name_indication(self.use_sni) .verify_hostname(!self.accept_invalid_hostnames); if self.accept_invalid_certs { ssl.set_verify(SslVerifyMode::NONE); } let s = ssl.connect(domain, stream)?; Ok(TlsStream(s)) } } #[derive(Clone)] pub struct TlsAcceptor(SslAcceptor); impl TlsAcceptor { pub fn new(builder: &TlsAcceptorBuilder) -> Result { let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; acceptor.set_private_key(&(builder.identity.0).0.pkey)?; acceptor.set_certificate(&(builder.identity.0).0.cert)?; if let Some(ref chain) = (builder.identity.0).0.chain { for cert in chain.iter().rev() { acceptor.add_extra_chain_cert(cert.to_owned())?; } } supported_protocols(builder.min_protocol, builder.max_protocol, &mut acceptor)?; Ok(TlsAcceptor(acceptor.build())) } pub fn accept(&self, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let s = self.0.accept(stream)?; Ok(TlsStream(s)) } } pub struct TlsStream(ssl::SslStream); impl fmt::Debug for TlsStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl TlsStream { pub fn get_ref(&self) -> &S { self.0.get_ref() } pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } pub fn buffered_read_size(&self) -> Result { Ok(self.0.ssl().pending()) } pub fn peer_certificate(&self) -> Result, Error> { Ok(self.0.ssl().peer_certificate().map(Certificate)) } pub fn tls_server_end_point(&self) -> Result>, Error> { let cert = if self.0.ssl().is_server() { self.0.ssl().certificate().map(|x| x.to_owned()) } else { self.0.ssl().peer_certificate() }; let cert = match cert { Some(cert) => cert, None => return Ok(None), }; let algo_nid = cert.signature_algorithm().object().nid(); let signature_algorithms = match algo_nid.signature_algorithms() { Some(algs) => algs, None => return Ok(None), }; let md = match signature_algorithms.digest { Nid::MD5 | Nid::SHA1 => MessageDigest::sha256(), nid => match MessageDigest::from_nid(nid) { Some(md) => md, None => return Ok(None), }, }; let digest = cert.digest(md)?; Ok(Some(digest.to_vec())) } pub fn shutdown(&mut self) -> io::Result<()> { match self.0.shutdown() { Ok(_) => Ok(()), Err(ref e) if e.code() == ssl::ErrorCode::ZERO_RETURN => Ok(()), Err(e) => Err(e .into_io_error() .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))), } } } impl io::Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } impl io::Write for TlsStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } native-tls-0.2.3/src/imp/schannel.rs010064400017500001750000000225501336312040100155350ustar0000000000000000extern crate schannel; use self::schannel::cert_context::{CertContext, HashAlgorithm}; use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions}; use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred}; use self::schannel::tls_stream; use std::error; use std::fmt; use std::io; use std::str; use {TlsAcceptorBuilder, TlsConnectorBuilder}; const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E; static PROTOCOLS: &'static [Protocol] = &[ Protocol::Ssl3, Protocol::Tls10, Protocol::Tls11, Protocol::Tls12, ]; fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] { let mut protocols = PROTOCOLS; if let Some(p) = max.and_then(|max| protocols.get(..max as usize)) { protocols = p; } if let Some(p) = min.and_then(|min| protocols.get(min as usize..)) { protocols = p; } protocols } pub struct Error(io::Error); impl error::Error for Error { fn description(&self) -> &str { error::Error::description(&self.0) } fn cause(&self) -> Option<&error::Error> { error::Error::cause(&self.0) } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) } } impl fmt::Debug for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl From for Error { fn from(error: io::Error) -> Error { Error(error) } } pub struct Identity { cert: CertContext, } impl Identity { pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result { let store = PfxImportOptions::new().password(pass).import(buf)?; let mut identity = None; for cert in store.certs() { if cert .private_key() .silent(true) .compare_key(true) .acquire() .is_ok() { identity = Some(cert); break; } } let identity = match identity { Some(identity) => identity, None => { return Err(io::Error::new( io::ErrorKind::InvalidInput, "No identity found in PKCS #12 archive", ).into()); } }; Ok(Identity { cert: identity }) } } #[derive(Clone)] pub struct Certificate(CertContext); impl Certificate { pub fn from_der(buf: &[u8]) -> Result { let cert = CertContext::new(buf)?; Ok(Certificate(cert)) } pub fn from_pem(buf: &[u8]) -> Result { match str::from_utf8(buf) { Ok(s) => { let cert = CertContext::from_pem(s)?; Ok(Certificate(cert)) } Err(_) => Err(io::Error::new( io::ErrorKind::InvalidInput, "PEM representation contains non-UTF-8 bytes", ).into()), } } pub fn to_der(&self) -> Result, Error> { Ok(self.0.to_der().to_vec()) } } pub struct MidHandshakeTlsStream(tls_stream::MidHandshakeTlsStream); impl fmt::Debug for MidHandshakeTlsStream where S: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl MidHandshakeTlsStream where S: io::Read + io::Write, { pub fn get_ref(&self) -> &S { self.0.get_ref() } pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } pub fn handshake(self) -> Result, HandshakeError> { match self.0.handshake() { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } pub enum HandshakeError { Failure(Error), WouldBlock(MidHandshakeTlsStream), } impl From> for HandshakeError { fn from(e: tls_stream::HandshakeError) -> HandshakeError { match e { tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()), tls_stream::HandshakeError::Interrupted(s) => { HandshakeError::WouldBlock(MidHandshakeTlsStream(s)) } } } } impl From for HandshakeError { fn from(e: io::Error) -> HandshakeError { HandshakeError::Failure(e.into()) } } #[derive(Clone)] pub struct TlsConnector { cert: Option, roots: CertStore, min_protocol: Option<::Protocol>, max_protocol: Option<::Protocol>, use_sni: bool, accept_invalid_hostnames: bool, accept_invalid_certs: bool, } impl TlsConnector { pub fn new(builder: &TlsConnectorBuilder) -> Result { let cert = builder.identity.as_ref().map(|i| i.0.cert.clone()); let mut roots = Memory::new()?.into_store(); for cert in &builder.root_certificates { roots.add_cert(&(cert.0).0, CertAdd::ReplaceExisting)?; } Ok(TlsConnector { cert, roots, min_protocol: builder.min_protocol, max_protocol: builder.max_protocol, use_sni: builder.use_sni, accept_invalid_hostnames: builder.accept_invalid_hostnames, accept_invalid_certs: builder.accept_invalid_certs, }) } pub fn connect(&self, domain: &str, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let mut builder = SchannelCred::builder(); builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol)); if let Some(cert) = self.cert.as_ref() { builder.cert(cert.clone()); } let cred = builder.acquire(Direction::Outbound)?; let mut builder = tls_stream::Builder::new(); builder .cert_store(self.roots.clone()) .domain(domain) .use_sni(self.use_sni) .accept_invalid_hostnames(self.accept_invalid_hostnames); if self.accept_invalid_certs { builder.verify_callback(|_| Ok(())); } match builder.connect(cred, stream) { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } #[derive(Clone)] pub struct TlsAcceptor { cert: CertContext, min_protocol: Option<::Protocol>, max_protocol: Option<::Protocol>, } impl TlsAcceptor { pub fn new(builder: &TlsAcceptorBuilder) -> Result { Ok(TlsAcceptor { cert: builder.identity.0.cert.clone(), min_protocol: builder.min_protocol, max_protocol: builder.max_protocol, }) } pub fn accept(&self, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let mut builder = SchannelCred::builder(); builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol)); builder.cert(self.cert.clone()); // FIXME we're probably missing the certificate chain? let cred = builder.acquire(Direction::Inbound)?; match tls_stream::Builder::new().accept(cred, stream) { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } pub struct TlsStream(tls_stream::TlsStream); impl fmt::Debug for TlsStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl TlsStream { pub fn get_ref(&self) -> &S { self.0.get_ref() } pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } pub fn buffered_read_size(&self) -> Result { Ok(self.0.get_buf().len()) } pub fn peer_certificate(&self) -> Result, Error> { match self.0.peer_certificate() { Ok(cert) => Ok(Some(Certificate(cert))), Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => Ok(None), Err(e) => Err(Error(e)), } } pub fn tls_server_end_point(&self) -> Result>, Error> { let cert = if self.0.is_server() { self.0.certificate() } else { self.0.peer_certificate() }; let cert = match cert { Ok(cert) => cert, Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => return Ok(None), Err(e) => return Err(Error(e)), }; let signature_algorithms = cert.sign_hash_algorithms()?; let hash = match signature_algorithms.rsplit('/').next().unwrap() { "MD5" | "SHA1" | "SHA256" => HashAlgorithm::sha256(), "SHA384" => HashAlgorithm::sha384(), "SHA512" => HashAlgorithm::sha512(), _ => return Ok(None), }; let digest = cert.fingerprint(hash)?; Ok(Some(digest)) } pub fn shutdown(&mut self) -> io::Result<()> { self.0.shutdown()?; Ok(()) } } impl io::Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } impl io::Write for TlsStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } native-tls-0.2.3/src/imp/security_framework.rs010064400017500001750000000413331336312040100176660ustar0000000000000000extern crate libc; extern crate security_framework; extern crate security_framework_sys; extern crate tempfile; use self::security_framework::base; use self::security_framework::certificate::SecCertificate; use self::security_framework::identity::SecIdentity; use self::security_framework::import_export::{ImportedIdentity, Pkcs12ImportOptions}; use self::security_framework::secure_transport::{ self, ClientBuilder, SslConnectionType, SslContext, SslProtocol, SslProtocolSide, }; use self::security_framework_sys::base::errSecIO; use self::tempfile::TempDir; use std::error; use std::fmt; use std::io; use std::sync::Mutex; use std::sync::{Once, ONCE_INIT}; #[cfg(not(target_os = "ios"))] use self::security_framework::os::macos::certificate::{PropertyType, SecCertificateExt}; #[cfg(not(target_os = "ios"))] use self::security_framework::os::macos::certificate_oids::CertificateOid; #[cfg(not(target_os = "ios"))] use self::security_framework::os::macos::import_export::{ImportOptions, SecItems}; #[cfg(not(target_os = "ios"))] use self::security_framework::os::macos::keychain::{self, KeychainSettings, SecKeychain}; #[cfg(not(target_os = "ios"))] use self::security_framework_sys::base::errSecParam; use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder}; static SET_AT_EXIT: Once = ONCE_INIT; #[cfg(not(target_os = "ios"))] lazy_static! { static ref TEMP_KEYCHAIN: Mutex> = Mutex::new(None); } fn convert_protocol(protocol: Protocol) -> SslProtocol { match protocol { Protocol::Sslv3 => SslProtocol::SSL3, Protocol::Tlsv10 => SslProtocol::TLS1, Protocol::Tlsv11 => SslProtocol::TLS11, Protocol::Tlsv12 => SslProtocol::TLS12, Protocol::__NonExhaustive => unreachable!(), } } pub struct Error(base::Error); impl error::Error for Error { fn description(&self) -> &str { error::Error::description(&self.0) } fn cause(&self) -> Option<&error::Error> { error::Error::cause(&self.0) } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) } } impl fmt::Debug for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl From for Error { fn from(error: base::Error) -> Error { Error(error) } } #[derive(Clone)] pub struct Identity { identity: SecIdentity, chain: Vec, } impl Identity { pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result { let mut imports = Identity::import_options(buf, pass)?; let import = imports.pop().unwrap(); let identity = import .identity .expect("Pkcs12 files must include an identity"); // FIXME: Compare the certificates for equality using CFEqual let identity_cert = identity.certificate()?.to_der(); Ok(Identity { identity: identity, chain: import .cert_chain .unwrap_or(vec![]) .into_iter() .filter(|c| c.to_der() != identity_cert) .collect(), }) } #[cfg(not(target_os = "ios"))] fn import_options(buf: &[u8], pass: &str) -> Result, Error> { SET_AT_EXIT.call_once(|| { extern "C" fn atexit() { *TEMP_KEYCHAIN.lock().unwrap() = None; } unsafe { libc::atexit(atexit); } }); let keychain = match *TEMP_KEYCHAIN.lock().unwrap() { Some((ref keychain, _)) => keychain.clone(), ref mut lock @ None => { let dir = TempDir::new().map_err(|_| Error(base::Error::from(errSecIO)))?; let mut keychain = keychain::CreateOptions::new() .password(pass) .create(dir.path().join("tmp.keychain"))?; keychain.set_settings(&KeychainSettings::new())?; *lock = Some((keychain.clone(), dir)); keychain } }; let imports = Pkcs12ImportOptions::new() .passphrase(pass) .keychain(keychain) .import(buf)?; Ok(imports) } #[cfg(target_os = "ios")] fn import_options(buf: &[u8], pass: &str) -> Result, Error> { let imports = Pkcs12ImportOptions::new().passphrase(pass).import(buf)?; Ok(imports) } } #[derive(Clone)] pub struct Certificate(SecCertificate); impl Certificate { pub fn from_der(buf: &[u8]) -> Result { let cert = SecCertificate::from_der(buf)?; Ok(Certificate(cert)) } #[cfg(not(target_os = "ios"))] pub fn from_pem(buf: &[u8]) -> Result { let mut items = SecItems::default(); ImportOptions::new().items(&mut items).import(buf)?; if items.certificates.len() == 1 && items.identities.is_empty() && items.keys.is_empty() { Ok(Certificate(items.certificates.pop().unwrap())) } else { Err(Error(base::Error::from(errSecParam))) } } #[cfg(target_os = "ios")] pub fn from_pem(buf: &[u8]) -> Result { panic!("Not implemented on iOS"); } pub fn to_der(&self) -> Result, Error> { Ok(self.0.to_der()) } } pub enum HandshakeError { WouldBlock(MidHandshakeTlsStream), Failure(Error), } impl From> for HandshakeError { fn from(e: secure_transport::ClientHandshakeError) -> HandshakeError { match e { secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()), secure_transport::ClientHandshakeError::Interrupted(s) => { HandshakeError::WouldBlock(MidHandshakeTlsStream::Client(s)) } } } } impl From for HandshakeError { fn from(e: base::Error) -> HandshakeError { HandshakeError::Failure(e.into()) } } pub enum MidHandshakeTlsStream { Server( secure_transport::MidHandshakeSslStream, Option, ), Client(secure_transport::MidHandshakeClientBuilder), } impl fmt::Debug for MidHandshakeTlsStream where S: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { MidHandshakeTlsStream::Server(ref s, _) => s.fmt(fmt), MidHandshakeTlsStream::Client(ref s) => s.fmt(fmt), } } } impl MidHandshakeTlsStream where S: io::Read + io::Write, { pub fn get_ref(&self) -> &S { match *self { MidHandshakeTlsStream::Server(ref s, _) => s.get_ref(), MidHandshakeTlsStream::Client(ref s) => s.get_ref(), } } pub fn get_mut(&mut self) -> &mut S { match *self { MidHandshakeTlsStream::Server(ref mut s, _) => s.get_mut(), MidHandshakeTlsStream::Client(ref mut s) => s.get_mut(), } } pub fn handshake(self) -> Result, HandshakeError> { match self { MidHandshakeTlsStream::Server(s, cert) => match s.handshake() { Ok(stream) => Ok(TlsStream { stream, cert }), Err(secure_transport::HandshakeError::Failure(e)) => { Err(HandshakeError::Failure(Error(e))) } Err(secure_transport::HandshakeError::Interrupted(s)) => Err( HandshakeError::WouldBlock(MidHandshakeTlsStream::Server(s, cert)), ), }, MidHandshakeTlsStream::Client(s) => match s.handshake() { Ok(stream) => Ok(TlsStream { stream, cert: None }), Err(e) => Err(e.into()), }, } } } #[derive(Clone)] pub struct TlsConnector { identity: Option, min_protocol: Option, max_protocol: Option, roots: Vec, use_sni: bool, danger_accept_invalid_hostnames: bool, danger_accept_invalid_certs: bool, } impl TlsConnector { pub fn new(builder: &TlsConnectorBuilder) -> Result { Ok(TlsConnector { identity: builder.identity.as_ref().map(|i| i.0.clone()), min_protocol: builder.min_protocol, max_protocol: builder.max_protocol, roots: builder .root_certificates .iter() .map(|c| (c.0).0.clone()) .collect(), use_sni: builder.use_sni, danger_accept_invalid_hostnames: builder.accept_invalid_hostnames, danger_accept_invalid_certs: builder.accept_invalid_certs, }) } pub fn connect(&self, domain: &str, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let mut builder = ClientBuilder::new(); if let Some(min) = self.min_protocol { builder.protocol_min(convert_protocol(min)); } if let Some(max) = self.max_protocol { builder.protocol_max(convert_protocol(max)); } if let Some(identity) = self.identity.as_ref() { builder.identity(&identity.identity, &identity.chain); } builder.anchor_certificates(&self.roots); builder.use_sni(self.use_sni); builder.danger_accept_invalid_hostnames(self.danger_accept_invalid_hostnames); builder.danger_accept_invalid_certs(self.danger_accept_invalid_certs); match builder.handshake(domain, stream) { Ok(stream) => Ok(TlsStream { stream, cert: None }), Err(e) => Err(e.into()), } } } #[derive(Clone)] pub struct TlsAcceptor { identity: Identity, min_protocol: Option, max_protocol: Option, } impl TlsAcceptor { pub fn new(builder: &TlsAcceptorBuilder) -> Result { Ok(TlsAcceptor { identity: builder.identity.0.clone(), min_protocol: builder.min_protocol, max_protocol: builder.max_protocol, }) } pub fn accept(&self, stream: S) -> Result, HandshakeError> where S: io::Read + io::Write, { let mut ctx = SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM)?; if let Some(min) = self.min_protocol { ctx.set_protocol_version_min(convert_protocol(min))?; } if let Some(max) = self.max_protocol { ctx.set_protocol_version_max(convert_protocol(max))?; } ctx.set_certificate(&self.identity.identity, &self.identity.chain)?; let cert = Some(self.identity.identity.certificate()?); match ctx.handshake(stream) { Ok(stream) => Ok(TlsStream { stream, cert }), Err(secure_transport::HandshakeError::Failure(e)) => { Err(HandshakeError::Failure(Error(e))) } Err(secure_transport::HandshakeError::Interrupted(s)) => Err( HandshakeError::WouldBlock(MidHandshakeTlsStream::Server(s, cert)), ), } } } pub struct TlsStream { stream: secure_transport::SslStream, cert: Option, } impl fmt::Debug for TlsStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.stream, fmt) } } impl TlsStream { pub fn get_ref(&self) -> &S { self.stream.get_ref() } pub fn get_mut(&mut self) -> &mut S { self.stream.get_mut() } pub fn buffered_read_size(&self) -> Result { Ok(self.stream.context().buffered_read_size()?) } pub fn peer_certificate(&self) -> Result, Error> { let trust = match self.stream.context().peer_trust2()? { Some(trust) => trust, None => return Ok(None), }; trust.evaluate()?; Ok(trust.certificate_at_index(0).map(Certificate)) } #[cfg(target_os = "ios")] pub fn tls_server_end_point(&self) -> Result>, Error> { Ok(None) } #[cfg(not(target_os = "ios"))] pub fn tls_server_end_point(&self) -> Result>, Error> { let cert = match self.cert { Some(ref cert) => cert.clone(), None => match self.peer_certificate()? { Some(cert) => cert.0, None => return Ok(None), }, }; let property = match cert .properties(Some(&[CertificateOid::x509_v1_signature_algorithm()])) .ok() .and_then(|p| p.get(CertificateOid::x509_v1_signature_algorithm())) { Some(property) => property, None => return Ok(None), }; let section = match property.get() { PropertyType::Section(section) => section, _ => return Ok(None), }; let algorithm = match section .iter() .filter(|p| p.label().to_string() == "Algorithm") .next() { Some(property) => property, None => return Ok(None), }; let algorithm = match algorithm.get() { PropertyType::String(algorithm) => algorithm, _ => return Ok(None), }; let digest = match &*algorithm.to_string() { // MD5 "1.2.840.113549.2.5" | "1.2.840.113549.1.1.4" | "1.3.14.3.2.3" => Digest::Sha256, // SHA-1 "1.3.14.3.2.26" | "1.3.14.3.2.15" | "1.2.840.113549.1.1.5" | "1.3.14.3.2.29" | "1.2.840.10040.4.3" | "1.3.14.3.2.13" | "1.2.840.10045.4.1" => Digest::Sha256, // SHA-224 "2.16.840.1.101.3.4.2.4" | "1.2.840.113549.1.1.14" | "2.16.840.1.101.3.4.3.1" | "1.2.840.10045.4.3.1" => Digest::Sha224, // SHA-256 "2.16.840.1.101.3.4.2.1" | "1.2.840.113549.1.1.11" | "1.2.840.10045.4.3.2" => { Digest::Sha256 } // SHA-384 "2.16.840.1.101.3.4.2.2" | "1.2.840.113549.1.1.12" | "1.2.840.10045.4.3.3" => { Digest::Sha384 } // SHA-512 "2.16.840.1.101.3.4.2.3" | "1.2.840.113549.1.1.13" | "1.2.840.10045.4.3.4" => { Digest::Sha512 } _ => return Ok(None), }; let der = cert.to_der(); Ok(Some(digest.hash(&der))) } pub fn shutdown(&mut self) -> io::Result<()> { self.stream.close()?; Ok(()) } } impl io::Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.stream.read(buf) } } impl io::Write for TlsStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.stream.write(buf) } fn flush(&mut self) -> io::Result<()> { self.stream.flush() } } enum Digest { Sha224, Sha256, Sha384, Sha512, } impl Digest { fn hash(&self, data: &[u8]) -> Vec { unsafe { assert!(data.len() <= CC_LONG::max_value() as usize); match *self { Digest::Sha224 => { let mut buf = [0; CC_SHA224_DIGEST_LENGTH]; CC_SHA224(data.as_ptr(), data.len() as CC_LONG, buf.as_mut_ptr()); buf.to_vec() } Digest::Sha256 => { let mut buf = [0; CC_SHA256_DIGEST_LENGTH]; CC_SHA256(data.as_ptr(), data.len() as CC_LONG, buf.as_mut_ptr()); buf.to_vec() } Digest::Sha384 => { let mut buf = [0; CC_SHA384_DIGEST_LENGTH]; CC_SHA384(data.as_ptr(), data.len() as CC_LONG, buf.as_mut_ptr()); buf.to_vec() } Digest::Sha512 => { let mut buf = [0; CC_SHA512_DIGEST_LENGTH]; CC_SHA512(data.as_ptr(), data.len() as CC_LONG, buf.as_mut_ptr()); buf.to_vec() } } } } } // FIXME ideally we'd pull these in from elsewhere const CC_SHA224_DIGEST_LENGTH: usize = 28; const CC_SHA256_DIGEST_LENGTH: usize = 32; const CC_SHA384_DIGEST_LENGTH: usize = 48; const CC_SHA512_DIGEST_LENGTH: usize = 64; #[allow(non_camel_case_types)] type CC_LONG = u32; extern "C" { fn CC_SHA224(data: *const u8, len: CC_LONG, md: *mut u8) -> *mut u8; fn CC_SHA256(data: *const u8, len: CC_LONG, md: *mut u8) -> *mut u8; fn CC_SHA384(data: *const u8, len: CC_LONG, md: *mut u8) -> *mut u8; fn CC_SHA512(data: *const u8, len: CC_LONG, md: *mut u8) -> *mut u8; } native-tls-0.2.3/src/lib.rs010064400017500001750000000512541343664654700137570ustar0000000000000000//! An abstraction over platform-specific TLS implementations. //! //! Many applications require TLS/SSL communication in one form or another as //! part of their implementation, but finding a library for this isn't always //! trivial! The purpose of this crate is to provide a seamless integration //! experience on all platforms with a cross-platform API that deals with all //! the underlying details for you. //! //! # How is this implemented? //! //! This crate uses SChannel on Windows (via the `schannel` crate), Secure //! Transport on OSX (via the `security-framework` crate), and OpenSSL (via the //! `openssl` crate) on all other platforms. Future futures may also enable //! other TLS frameworks as well, but these initial libraries are likely to //! remain as the defaults. //! //! Note that this crate also strives to be secure-by-default. For example when //! using OpenSSL it will configure validation callbacks to ensure that //! hostnames match certificates, use strong ciphers, etc. This implies that //! this crate is *not* just a thin abstraction around the underlying libraries, //! but also an implementation that strives to strike reasonable defaults. //! //! # Supported features //! //! This crate supports the following features out of the box: //! //! * TLS/SSL client communication //! * TLS/SSL server communication //! * PKCS#12 encoded identities //! * Secure-by-default for client and server //! * Includes hostname verification for clients //! * Supports asynchronous I/O for both the server and the client //! //! # Cargo Features //! //! * `vendored` - If enabled, the crate will compile and statically link to a //! vendored copy of OpenSSL. This feature has no effect on Windows and //! macOS, where OpenSSL is not used. //! //! # Examples //! //! To connect as a client to a remote server: //! //! ```rust //! use native_tls::TlsConnector; //! use std::io::{Read, Write}; //! use std::net::TcpStream; //! //! let connector = TlsConnector::new().unwrap(); //! //! let stream = TcpStream::connect("google.com:443").unwrap(); //! let mut stream = connector.connect("google.com", stream).unwrap(); //! //! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); //! let mut res = vec![]; //! stream.read_to_end(&mut res).unwrap(); //! println!("{}", String::from_utf8_lossy(&res)); //! ``` //! //! To accept connections as a server from remote clients: //! //! ```rust,no_run //! use native_tls::{Identity, TlsAcceptor, TlsStream}; //! use std::fs::File; //! use std::io::{Read}; //! use std::net::{TcpListener, TcpStream}; //! use std::sync::Arc; //! use std::thread; //! //! let mut file = File::open("identity.pfx").unwrap(); //! let mut identity = vec![]; //! file.read_to_end(&mut identity).unwrap(); //! let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap(); //! //! let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); //! let acceptor = TlsAcceptor::new(identity).unwrap(); //! let acceptor = Arc::new(acceptor); //! //! fn handle_client(stream: TlsStream) { //! // ... //! } //! //! for stream in listener.incoming() { //! match stream { //! Ok(stream) => { //! let acceptor = acceptor.clone(); //! thread::spawn(move || { //! let stream = acceptor.accept(stream).unwrap(); //! handle_client(stream); //! }); //! } //! Err(e) => { /* connection failed */ } //! } //! } //! ``` #![doc(html_root_url = "https://docs.rs/native-tls/0.2")] #![warn(missing_docs)] #[macro_use] #[cfg(any(target_os = "macos", target_os = "ios"))] extern crate lazy_static; #[cfg(test)] extern crate hex; use std::any::Any; use std::error; use std::fmt; use std::io; use std::result; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[macro_use] extern crate log; #[cfg(any(target_os = "macos", target_os = "ios"))] #[path = "imp/security_framework.rs"] mod imp; #[cfg(target_os = "windows")] #[path = "imp/schannel.rs"] mod imp; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[path = "imp/openssl.rs"] mod imp; #[cfg(test)] mod test; /// A typedef of the result-type returned by many methods. pub type Result = result::Result; /// An error returned from the TLS implementation. pub struct Error(imp::Error); impl error::Error for Error { fn description(&self) -> &str { error::Error::description(&self.0) } fn cause(&self) -> Option<&error::Error> { error::Error::cause(&self.0) } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) } } impl fmt::Debug for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl From for Error { fn from(err: imp::Error) -> Error { Error(err) } } /// A cryptographic identity. /// /// An identity is an X509 certificate along with its corresponding private key and chain of certificates to a trusted /// root. pub struct Identity(imp::Identity); impl Identity { /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key. /// /// The archive should contain a leaf certificate and its private key, as well any intermediate /// certificates that should be sent to clients to allow them to build a chain to a trusted /// root. The chain certificates should be in order from the leaf certificate towards the root. /// /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created /// with the OpenSSL `pkcs12` tool: /// /// ```bash /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem /// ``` pub fn from_pkcs12(der: &[u8], password: &str) -> Result { let identity = imp::Identity::from_pkcs12(der, password)?; Ok(Identity(identity)) } } /// An X509 certificate. #[derive(Clone)] pub struct Certificate(imp::Certificate); impl Certificate { /// Parses a DER-formatted X509 certificate. pub fn from_der(der: &[u8]) -> Result { let cert = imp::Certificate::from_der(der)?; Ok(Certificate(cert)) } /// Parses a PEM-formatted X509 certificate. pub fn from_pem(der: &[u8]) -> Result { let cert = imp::Certificate::from_pem(der)?; Ok(Certificate(cert)) } /// Returns the DER-encoded representation of this certificate. pub fn to_der(&self) -> Result> { let der = self.0.to_der()?; Ok(der) } } /// A TLS stream which has been interrupted midway through the handshake process. pub struct MidHandshakeTlsStream(imp::MidHandshakeTlsStream); impl fmt::Debug for MidHandshakeTlsStream where S: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl MidHandshakeTlsStream where S: io::Read + io::Write, { /// Returns a shared reference to the inner stream. pub fn get_ref(&self) -> &S { self.0.get_ref() } /// Returns a mutable reference to the inner stream. pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } /// Restarts the handshake process. /// /// If the handshake completes successfully then the negotiated stream is /// returned. If there is a problem, however, then an error is returned. /// Note that the error may not be fatal. For example if the underlying /// stream is an asynchronous one then `HandshakeError::WouldBlock` may /// just mean to wait for more I/O to happen later. pub fn handshake(self) -> result::Result, HandshakeError> { match self.0.handshake() { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } /// An error returned from `ClientBuilder::handshake`. #[derive(Debug)] pub enum HandshakeError { /// A fatal error. Failure(Error), /// A stream interrupted midway through the handshake process due to a /// `WouldBlock` error. /// /// Note that this is not a fatal error and it should be safe to call /// `handshake` at a later time once the stream is ready to perform I/O /// again. WouldBlock(MidHandshakeTlsStream), } impl error::Error for HandshakeError where S: Any + fmt::Debug, { fn description(&self) -> &str { "handshake error" } fn cause(&self) -> Option<&error::Error> { match *self { HandshakeError::Failure(ref e) => Some(e), HandshakeError::WouldBlock(_) => None, } } } impl fmt::Display for HandshakeError where S: Any + fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { HandshakeError::Failure(ref e) => fmt::Display::fmt(e, fmt), HandshakeError::WouldBlock(_) => fmt.write_str("the handshake process was interrupted"), } } } impl From> for HandshakeError { fn from(e: imp::HandshakeError) -> HandshakeError { match e { imp::HandshakeError::Failure(e) => HandshakeError::Failure(Error(e)), imp::HandshakeError::WouldBlock(s) => { HandshakeError::WouldBlock(MidHandshakeTlsStream(s)) } } } } /// SSL/TLS protocol versions. #[derive(Debug, Copy, Clone)] pub enum Protocol { /// The SSL 3.0 protocol. /// /// # Warning /// /// SSL 3.0 has severe security flaws, and should not be used unless absolutely necessary. If /// you are not sure if you need to enable this protocol, you should not. Sslv3, /// The TLS 1.0 protocol. Tlsv10, /// The TLS 1.1 protocol. Tlsv11, /// The TLS 1.2 protocol. Tlsv12, #[doc(hidden)] __NonExhaustive, } /// A builder for `TlsConnector`s. pub struct TlsConnectorBuilder { identity: Option, min_protocol: Option, max_protocol: Option, root_certificates: Vec, accept_invalid_certs: bool, accept_invalid_hostnames: bool, use_sni: bool, } impl TlsConnectorBuilder { /// Sets the identity to be used for client certificate authentication. pub fn identity(&mut self, identity: Identity) -> &mut TlsConnectorBuilder { self.identity = Some(identity); self } /// Sets the minimum supported protocol version. /// /// A value of `None` enables support for the oldest protocols supported by the implementation. /// /// Defaults to `Some(Protocol::Tlsv10)`. pub fn min_protocol_version(&mut self, protocol: Option) -> &mut TlsConnectorBuilder { self.min_protocol = protocol; self } /// Sets the maximum supported protocol version. /// /// A value of `None` enables support for the newest protocols supported by the implementation. /// /// Defaults to `None`. pub fn max_protocol_version(&mut self, protocol: Option) -> &mut TlsConnectorBuilder { self.max_protocol = protocol; self } /// Adds a certificate to the set of roots that the connector will trust. /// /// The connector will use the system's trust root by default. This method can be used to add /// to that set when communicating with servers not trusted by the system. /// /// Defaults to an empty set. pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut TlsConnectorBuilder { self.root_certificates.push(cert); self } /// Controls the use of certificate validation. /// /// Defaults to `false`. /// /// # Warning /// /// You should think very carefully before using this method. If invalid certificates are trusted, *any* /// certificate for *any* site will be trusted for use. This includes expired certificates. This introduces /// significant vulnerabilities, and should only be used as a last resort. pub fn danger_accept_invalid_certs( &mut self, accept_invalid_certs: bool, ) -> &mut TlsConnectorBuilder { self.accept_invalid_certs = accept_invalid_certs; self } /// Controls the use of Server Name Indication (SNI). /// /// Defaults to `true`. pub fn use_sni(&mut self, use_sni: bool) -> &mut TlsConnectorBuilder { self.use_sni = use_sni; self } /// Controls the use of hostname verification. /// /// Defaults to `false`. /// /// # Warning /// /// You should think very carefully before using this method. If invalid hostnames are trusted, *any* valid /// certificate for *any* site will be trusted for use. This introduces significant vulnerabilities, and should /// only be used as a last resort. pub fn danger_accept_invalid_hostnames( &mut self, accept_invalid_hostnames: bool, ) -> &mut TlsConnectorBuilder { self.accept_invalid_hostnames = accept_invalid_hostnames; self } /// Creates a new `TlsConnector`. pub fn build(&self) -> Result { let connector = imp::TlsConnector::new(self)?; Ok(TlsConnector(connector)) } } /// A builder for client-side TLS connections. /// /// # Examples /// /// ```rust /// use native_tls::TlsConnector; /// use std::io::{Read, Write}; /// use std::net::TcpStream; /// /// let connector = TlsConnector::new().unwrap(); /// /// let stream = TcpStream::connect("google.com:443").unwrap(); /// let mut stream = connector.connect("google.com", stream).unwrap(); /// /// stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); /// let mut res = vec![]; /// stream.read_to_end(&mut res).unwrap(); /// println!("{}", String::from_utf8_lossy(&res)); /// ``` #[derive(Clone)] pub struct TlsConnector(imp::TlsConnector); impl TlsConnector { /// Returns a new connector with default settings. pub fn new() -> Result { TlsConnector::builder().build() } /// Returns a new builder for a `TlsConnector`. pub fn builder() -> TlsConnectorBuilder { TlsConnectorBuilder { identity: None, min_protocol: Some(Protocol::Tlsv10), max_protocol: None, root_certificates: vec![], use_sni: true, accept_invalid_certs: false, accept_invalid_hostnames: false, } } /// Initiates a TLS handshake. /// /// The provided domain will be used for both SNI and certificate hostname /// validation. /// /// If the socket is nonblocking and a `WouldBlock` error is returned during /// the handshake, a `HandshakeError::WouldBlock` error will be returned /// which can be used to restart the handshake when the socket is ready /// again. /// /// The domain is ignored if both SNI and hostname verification are /// disabled. pub fn connect( &self, domain: &str, stream: S, ) -> result::Result, HandshakeError> where S: io::Read + io::Write, { let s = self.0.connect(domain, stream)?; Ok(TlsStream(s)) } } /// A builder for `TlsAcceptor`s. pub struct TlsAcceptorBuilder { identity: Identity, min_protocol: Option, max_protocol: Option, } impl TlsAcceptorBuilder { /// Sets the minimum supported protocol version. /// /// A value of `None` enables support for the oldest protocols supported by the implementation. /// /// Defaults to `Some(Protocol::Tlsv10)`. pub fn min_protocol_version(&mut self, protocol: Option) -> &mut TlsAcceptorBuilder { self.min_protocol = protocol; self } /// Sets the maximum supported protocol version. /// /// A value of `None` enables support for the newest protocols supported by the implementation. /// /// Defaults to `None`. pub fn max_protocol_version(&mut self, protocol: Option) -> &mut TlsAcceptorBuilder { self.max_protocol = protocol; self } /// Creates a new `TlsAcceptor`. pub fn build(&self) -> Result { let acceptor = imp::TlsAcceptor::new(self)?; Ok(TlsAcceptor(acceptor)) } } /// A builder for server-side TLS connections. /// /// # Examples /// /// ```rust,no_run /// use native_tls::{Identity, TlsAcceptor, TlsStream}; /// use std::fs::File; /// use std::io::{Read}; /// use std::net::{TcpListener, TcpStream}; /// use std::sync::Arc; /// use std::thread; /// /// let mut file = File::open("identity.pfx").unwrap(); /// let mut identity = vec![]; /// file.read_to_end(&mut identity).unwrap(); /// let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap(); /// /// let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); /// let acceptor = TlsAcceptor::new(identity).unwrap(); /// let acceptor = Arc::new(acceptor); /// /// fn handle_client(stream: TlsStream) { /// // ... /// } /// /// for stream in listener.incoming() { /// match stream { /// Ok(stream) => { /// let acceptor = acceptor.clone(); /// thread::spawn(move || { /// let stream = acceptor.accept(stream).unwrap(); /// handle_client(stream); /// }); /// } /// Err(e) => { /* connection failed */ } /// } /// } /// ``` #[derive(Clone)] pub struct TlsAcceptor(imp::TlsAcceptor); impl TlsAcceptor { /// Creates a acceptor with default settings. /// /// The identity acts as the server's private key/certificate chain. pub fn new(identity: Identity) -> Result { TlsAcceptor::builder(identity).build() } /// Returns a new builder for a `TlsAcceptor`. /// /// The identity acts as the server's private key/certificate chain. pub fn builder(identity: Identity) -> TlsAcceptorBuilder { TlsAcceptorBuilder { identity, min_protocol: Some(Protocol::Tlsv10), max_protocol: None, } } /// Initiates a TLS handshake. /// /// If the socket is nonblocking and a `WouldBlock` error is returned during /// the handshake, a `HandshakeError::WouldBlock` error will be returned /// which can be used to restart the handshake when the socket is ready /// again. pub fn accept(&self, stream: S) -> result::Result, HandshakeError> where S: io::Read + io::Write, { match self.0.accept(stream) { Ok(s) => Ok(TlsStream(s)), Err(e) => Err(e.into()), } } } /// A stream managing a TLS session. pub struct TlsStream(imp::TlsStream); impl fmt::Debug for TlsStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl TlsStream { /// Returns a shared reference to the inner stream. pub fn get_ref(&self) -> &S { self.0.get_ref() } /// Returns a mutable reference to the inner stream. pub fn get_mut(&mut self) -> &mut S { self.0.get_mut() } /// Returns the number of bytes that can be read without resulting in any /// network calls. pub fn buffered_read_size(&self) -> Result { Ok(self.0.buffered_read_size()?) } /// Returns the peer's leaf certificate, if available. pub fn peer_certificate(&self) -> Result> { Ok(self.0.peer_certificate()?.map(Certificate)) } /// Returns the tls-server-end-point channel binding data as defined in [RFC 5929]. /// /// [RFC 5929]: https://tools.ietf.org/html/rfc5929 pub fn tls_server_end_point(&self) -> Result>> { Ok(self.0.tls_server_end_point()?) } /// Shuts down the TLS session. pub fn shutdown(&mut self) -> io::Result<()> { self.0.shutdown()?; Ok(()) } } impl io::Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } impl io::Write for TlsStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } fn _check_kinds() { use std::net::TcpStream; fn is_sync() {} fn is_send() {} is_sync::(); is_send::(); is_sync::(); is_send::(); is_sync::(); is_send::(); is_sync::(); is_send::(); is_sync::(); is_send::(); is_sync::>(); is_send::>(); is_sync::>(); is_send::>(); } native-tls-0.2.3/src/test.rs010064400017500001750000000301451336312040100141330ustar0000000000000000use hex; #[allow(unused_imports)] use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::thread; use super::*; macro_rules! p { ($e:expr) => { match $e { Ok(r) => r, Err(e) => panic!("{:?}", e), } }; } // This nested mod is needed for ios testing with rust-test-ios mod tests { use super::*; #[test] fn connect_google() { let builder = p!(TlsConnector::new()); let s = p!(TcpStream::connect("google.com:443")); let mut socket = p!(builder.connect("google.com", s)); p!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n")); let mut result = vec![]; p!(socket.read_to_end(&mut result)); println!("{}", String::from_utf8_lossy(&result)); assert!(result.starts_with(b"HTTP/1.0")); assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); } #[test] fn connect_bad_hostname() { let builder = p!(TlsConnector::new()); let s = p!(TcpStream::connect("google.com:443")); builder.connect("goggle.com", s).unwrap_err(); } #[test] fn connect_bad_hostname_ignored() { let builder = p!(TlsConnector::builder() .danger_accept_invalid_hostnames(true) .build()); let s = p!(TcpStream::connect("google.com:443")); builder.connect("goggle.com", s).unwrap(); } #[test] fn server() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); p!(socket.write_all(b"world")); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); p!(socket.write_all(b"hello")); let mut buf = vec![]; p!(socket.read_to_end(&mut buf)); assert_eq!(buf, b"world"); p!(j.join()); } #[test] #[cfg(not(target_os = "ios"))] fn server_pem() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); p!(socket.write_all(b"world")); }); let root_ca = include_bytes!("../test/root-ca.pem"); let root_ca = Certificate::from_pem(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); p!(socket.write_all(b"hello")); let mut buf = vec![]; p!(socket.read_to_end(&mut buf)); assert_eq!(buf, b"world"); p!(j.join()); } #[test] fn peer_certificate() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let socket = p!(builder.accept(socket)); assert!(socket.peer_certificate().unwrap().is_none()); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .build()); let socket = p!(builder.connect("foobar.com", socket)); let cert_der = include_bytes!("../test/cert.der"); let cert = socket.peer_certificate().unwrap().unwrap(); assert_eq!(cert.to_der().unwrap(), &cert_der[..]); p!(j.join()); } #[test] fn server_tls11_only() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::builder(identity) .min_protocol_version(Some(Protocol::Tlsv11)) .max_protocol_version(Some(Protocol::Tlsv11)) .build()); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); p!(socket.write_all(b"world")); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .min_protocol_version(Some(Protocol::Tlsv11)) .max_protocol_version(Some(Protocol::Tlsv11)) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); p!(socket.write_all(b"hello")); let mut buf = vec![]; p!(socket.read_to_end(&mut buf)); assert_eq!(buf, b"world"); p!(j.join()); } #[test] fn server_no_shared_protocol() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::builder(identity) .min_protocol_version(Some(Protocol::Tlsv12)) .build()); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; assert!(builder.accept(socket).is_err()); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .max_protocol_version(Some(Protocol::Tlsv11)) .build()); assert!(builder.connect("foobar.com", socket).is_err()); p!(j.join()); } #[test] fn server_untrusted() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; // FIXME should assert error // https://github.com/steffengy/schannel-rs/issues/20 let _ = builder.accept(socket); }); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::new()); builder.connect("foobar.com", socket).unwrap_err(); p!(j.join()); } #[test] fn server_untrusted_unverified() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); p!(socket.write_all(b"world")); }); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .danger_accept_invalid_certs(true) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); p!(socket.write_all(b"hello")); let mut buf = vec![]; p!(socket.read_to_end(&mut buf)); assert_eq!(buf, b"world"); p!(j.join()); } #[test] fn import_same_identity_multiple_times() { let buf = include_bytes!("../test/identity.p12"); let _ = p!(Identity::from_pkcs12(buf, "mypass")); let _ = p!(Identity::from_pkcs12(buf, "mypass")); } #[test] fn shutdown() { let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); assert_eq!(p!(socket.read(&mut buf)), 0); p!(socket.shutdown()); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); p!(socket.write_all(b"hello")); p!(socket.shutdown()); p!(j.join()); } #[test] #[cfg_attr(target_os = "ios", ignore)] fn tls_server_end_point() { let expected = "4712b939fbcb42a6b5101b42139a25b14f81b418facabd378746f12f85cc6544"; let buf = include_bytes!("../test/identity.p12"); let identity = p!(Identity::from_pkcs12(buf, "mypass")); let builder = p!(TlsAcceptor::new(identity)); let listener = p!(TcpListener::bind("0.0.0.0:0")); let port = p!(listener.local_addr()).port(); let j = thread::spawn(move || { let socket = p!(listener.accept()).0; let mut socket = p!(builder.accept(socket)); let binding = socket.tls_server_end_point().unwrap().unwrap(); assert_eq!(hex::encode(binding), expected); let mut buf = [0; 5]; p!(socket.read_exact(&mut buf)); assert_eq!(&buf, b"hello"); p!(socket.write_all(b"world")); }); let root_ca = include_bytes!("../test/root-ca.der"); let root_ca = Certificate::from_der(root_ca).unwrap(); let socket = p!(TcpStream::connect(("localhost", port))); let builder = p!(TlsConnector::builder() .add_root_certificate(root_ca) .build()); let mut socket = p!(builder.connect("foobar.com", socket)); let binding = socket.tls_server_end_point().unwrap().unwrap(); assert_eq!(hex::encode(binding), expected); p!(socket.write_all(b"hello")); let mut buf = vec![]; p!(socket.read_to_end(&mut buf)); assert_eq!(buf, b"world"); p!(j.join()); } } native-tls-0.2.3/test/cert.der010064400017500001750000000014371336312040100144310ustar000000000000000000 q/0  *H  0E1 0 UAU10U Some-State1!0U Internet Widgits Pty Ltd0 160814170003Z 260812170003Z0Z1 0 UAU10U Some-State1!0U Internet Widgits Pty Ltd10U foobar.com0"0  *H 0 %DP#x6I-Gl6?7޿3eJI"do 4N?rPК6bOBt _Z%%5SR!W.xN:HexYT`^U|렑Ԓ'٫ fgړ  UሹIleFl;nvz=N׌KRV^zd3Qݔܤ߄NPY\Э;6* 50  *H  Jy CGBϥQ͸а4 )Xk>I# ք%Wn!Re-N/fqG.*< J&'Iqʦ-4f'R99H)OXϰ3~bVXv3` iP,NbG h3d߲A1 )}6u@"OHx'TAre"6؃Hk^"\ձW2g_?*0SQQl F/sng^$3Ĭh'0p|.22*c K~"bgY*Tn̤zzR9r(7m૘zm Wн3-P}HԊ@_z4Di۩rpg$T#E$L =/M4 q`ИRΏ&7k'*~ݏgzcؚq@Ά)UrnXΧW,.M=9sIOXO #I͊ ȸҝ\:2S1؏_j:Purd+8p=,2ZdSMKPf|#.+";נ"$K_\>%]o LBG 5Ut!uoPfd6 ZIZeXfO^K'hpIY= 0U-uh1Gx|C["y6]C\ Nֺ OV:6bGUʶ+=,.{*2Н$6AXKS2>9RLw6Ӆn~x7*-btJ֛-rwn n u13+$)u7 XD/gZ(DY: I[N'h㾯38H |v0e4 ͍@)MZw oy=Oru7=E5_BσRYP {h%s.oY4gaɠXs)[>UdѰ 1C6qJ׽nEʹ0kVEG>,QXA*ZF4GXduȞvu[Jdʘb"MHufQPXf=7h3tᆳHr&2)TTt"^=@ӕ(G/;z-%3Xbijk&nRP 8σzHX4GC؁ʥMshՉR/uȮ ]d( N U|5u[?DSbǤVE 9G4g0f *H WS0O0K *H  00 *H  0iK[Kq=7_޼5Ue eTJc,v@D$COHpȮxM?͒i@ g*V@6>p5e OHݢfOg<`Q =S;]ZhPO@=:lpYĥe mE4oeD cMz環= 쩉q膔D"%aZG7kqv|sc"mwOŀ%ʱw3Vģ?.Z[dk;g2M5O6rdYNc;.59:7_IHR Ui9"־I+{azw}Ed||yԨZ4#.l$2l;D=;JH#Ǻ1>Z }y:֔ ;2Q Bi|KVA SCZ ^1s^Zk# ަ+>/^w;,]=^if ,Xǜ@gkWt!qЎiu4bYW&Y"Cb ;\RV) KkCp@QQ苇Zˡٿ:4z[< %}v6=O6 saBvȄ9[КQ~ (V-qϒ:Gnнҡ"M5!/Za` =&}S|m B`-H6~=$$!k[&pQxShtn+hW:f3>s26aK3}OV"poYus!㿬a5 sI;&^ݭ"g2@<]~ 9,fy!#PQ#ޛugsOe`#Cc=>ڈ1J0# *H  1foobar.com0# *H  1Y-DY'gn010!0 +d)II Z)Z#Fճ"onative-tls-0.2.3/test/root-ca.der010064400017500001750000000015411336312040100150340ustar00000000000000000]0E /%[Ǖ0  *H  0E1 0 UAU10U Some-State1!0U Internet Widgits Pty Ltd0 160814165611Z 260812165611Z0E1 0 UAU10U Some-State1!0U Internet Widgits Pty Ltd0"0  *H 0 Q~v-u^ۙRg ا0-GAZԬf[|vT-Dz'W<~sD] WxcT/sm~Ҳιćx]z_Яf+*{R8jɪy%9y9Qe<$m|DVdUvhWJ_#h)ޥut@*ܰa,sSz\PǧWtriPң_cP0N0Ulӥ _,ɍw70U#0lӥ _,ɍw70 U00  *H  UVdQ<:M g7~ ͼ )كl6*8(BjYlb2яGG!#͠&%kUH}}M]Hw28Բ”]ȍ`hj}\c#mPG&ERվ7z",zp[Imy]^ ~ĄI3native-tls-0.2.3/test/root-ca.pem010064400017500001750000000023151336312040100150430ustar0000000000000000-----BEGIN CERTIFICATE----- MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr 7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 GA== -----END CERTIFICATE----- native-tls-0.2.3/tests-ios/prelude.rs010064400017500001750000000004451336312040100157570ustar0000000000000000extern crate hex; extern crate native_tls; pub use native_tls::*; use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::thread; macro_rules! p { ($e:expr) => { match $e { Ok(r) => r, Err(e) => panic!("{:?}", e), } }; } native-tls-0.2.3/.cargo_vcs_info.json0000644000000001120000000000000131550ustar00{ "git": { "sha1": "ec1f5d2b8a9a88460487ffb6042b27f553fd57fc" } }