diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
commit | 023939b627b7dc93b01471f7d41fb8553ddb4ffa (patch) | |
tree | 60fc59477c605c72b0a1051409062ddecc43f877 /vendor/native-tls | |
parent | Adding debian version 1.72.1+dfsg1-1. (diff) | |
download | rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.tar.xz rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/native-tls')
-rw-r--r-- | vendor/native-tls/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/native-tls/CHANGELOG.md | 139 | ||||
-rw-r--r-- | vendor/native-tls/Cargo.lock | 447 | ||||
-rw-r--r-- | vendor/native-tls/Cargo.toml | 66 | ||||
-rw-r--r-- | vendor/native-tls/LICENSE-APACHE | 202 | ||||
-rw-r--r-- | vendor/native-tls/LICENSE-MIT | 19 | ||||
-rw-r--r-- | vendor/native-tls/README.md | 95 | ||||
-rw-r--r-- | vendor/native-tls/build.rs | 19 | ||||
-rw-r--r-- | vendor/native-tls/examples/google-connect.rs | 17 | ||||
-rw-r--r-- | vendor/native-tls/examples/simple-server-pkcs8.rs | 45 | ||||
-rw-r--r-- | vendor/native-tls/examples/simple-server.rs | 37 | ||||
-rw-r--r-- | vendor/native-tls/src/imp/openssl.rs | 483 | ||||
-rw-r--r-- | vendor/native-tls/src/imp/schannel.rs | 562 | ||||
-rw-r--r-- | vendor/native-tls/src/imp/security_framework.rs | 632 | ||||
-rw-r--r-- | vendor/native-tls/src/lib.rs | 721 | ||||
-rw-r--r-- | vendor/native-tls/src/test.rs | 573 |
16 files changed, 4058 insertions, 0 deletions
diff --git a/vendor/native-tls/.cargo-checksum.json b/vendor/native-tls/.cargo-checksum.json new file mode 100644 index 000000000..9af8ee82c --- /dev/null +++ b/vendor/native-tls/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"3ec2dc79aba726b64c4b30dfbfc832acf1dbcb22a91fbcf9989d24b27d42ed87","Cargo.lock":"9a694aa99425505811f81513a1ab786514a9d418be87d05e1de1053a0d3dddca","Cargo.toml":"71f6d31fc79fd032f3920999e255f62020554bae57022fd44d444afdf131b988","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"f2ad7982ddbfa8c45eb965315718c55b70b9cc311b49afacb50d5fe84173ae2c","README.md":"265b94051f44c7a12669864d75ab8c80a676070cb32e2209f74a320d84b38736","build.rs":"46cfae159ca20d37b04f4250eb538e60dc1981c39133f98a3e7691f002a18b91","examples/google-connect.rs":"339a8cb9c9b8c472f8cac96711950114528c8700e5e48d6f61d59b636b69eb0c","examples/simple-server-pkcs8.rs":"e7749791e5d15334a8d54a86df11fbe0167f3a4a6d2faa44560d26f92211cf23","examples/simple-server.rs":"f26fdcca641d03ecb7881fbc94ed1b5fe5db38531c1e8b4cd4a6bed3e3653ee8","src/imp/openssl.rs":"a5f46146e76325bfa2cdb03c6f600b2bd30417a3d05ea6cd7e36de4954e051e4","src/imp/schannel.rs":"c5a986eace90d5e020d19452f9a7a34693b4a137492a5290ce5a76388fd3fcbc","src/imp/security_framework.rs":"a4f70acc9f52c8126c212aea3e53d7dd62e637ab9ef8358486ba6f4583ea275a","src/lib.rs":"ab2e057f0fdfb01f12100ed7459ae192a64376759dccdc6a91b632ddbcfce98e","src/test.rs":"5cac53231672ef2a329e7a862abe7a9c1ba6f02bacbd0a57998e5453ad29d098"},"package":"07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"}
\ No newline at end of file diff --git a/vendor/native-tls/CHANGELOG.md b/vendor/native-tls/CHANGELOG.md new file mode 100644 index 000000000..40c4ddcd3 --- /dev/null +++ b/vendor/native-tls/CHANGELOG.md @@ -0,0 +1,139 @@ +# Change Log + +## [Unreleased] + +## [v0.2.11] + +### Fixed + +* Removed an unused build dependency. + +## [v0.2.10] + +### Fixed + +* Fixed the build for iOS. + +## [v0.2.9] + +### Added + +* Added `Identity::from_pkcs8`. + +## [v0.2.8] + +### Fixed + +* Fixed an off by one error in the schannel backend's handling of max_protocol_version. + +## [v0.2.7] + +### Added + +* Added support for ALPN in client APIs flagged under the `alpn` Cargo feature. + +## [v0.2.6] + +### Fixed + +* Fixed compilation on iOS. + +## [v0.2.5] + +### Added + +* Added `TlsConnectorBuilder::disable_built_in_roots` to only trust root certificates explicitly + added to the builder. + +### Updated + +* Updated security-framework to 2.0. + +## [v0.2.4] + +### Added + +* Added a `Clone` implementation for `Identity`. + +### Updated + +* Updated security-framework to 0.4. + +## [v0.2.3] + +### Fixed + +* Adding an already-trusted certificate to the root certificate set no longer triggers an error + with OpenSSL. + +### Updated + +* Updated security-framework to 0.3. + +## [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.11...master +[v0.2.11]: https://github.com/sfackler/rust-native-tls/compare/v0.2.10...v0.2.11 +[v0.2.10]: https://github.com/sfackler/rust-native-tls/compare/v0.2.9...v0.2.10 +[v0.2.9]: https://github.com/sfackler/rust-native-tls/compare/v0.2.8...v0.2.9 +[v0.2.8]: https://github.com/sfackler/rust-native-tls/compare/v0.2.7...v0.2.8 +[v0.2.7]: https://github.com/sfackler/rust-native-tls/compare/v0.2.6...v0.2.7 +[v0.2.6]: https://github.com/sfackler/rust-native-tls/compare/v0.2.5...v0.2.6 +[v0.2.5]: https://github.com/sfackler/rust-native-tls/compare/v0.2.4...v0.2.5 +[v0.2.4]: https://github.com/sfackler/rust-native-tls/compare/v0.2.3...v0.2.4 +[v0.2.3]: https://github.com/sfackler/rust-native-tls/compare/v0.2.2...v0.2.3 +[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 diff --git a/vendor/native-tls/Cargo.lock b/vendor/native-tls/Cargo.lock new file mode 100644 index 000000000..97c920af0 --- /dev/null +++ b/vendor/native-tls/Cargo.lock @@ -0,0 +1,447 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", + "test-cert-gen", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "111.24.0+1.1.1s" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +dependencies = [ + "autocfg", + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand", + "remove_dir_all", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "test-cert-gen" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3208d0ae2e3736d4ac2f6ba2229c4d9bbd54080e228e662a7684eabcf13ff419" +dependencies = [ + "pem", + "tempdir", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/vendor/native-tls/Cargo.toml b/vendor/native-tls/Cargo.toml new file mode 100644 index 000000000..a0592366c --- /dev/null +++ b/vendor/native-tls/Cargo.toml @@ -0,0 +1,66 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "native-tls" +version = "0.2.11" +authors = ["Steven Fackler <sfackler@gmail.com>"] +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" + +[package.metadata.docs.rs] +features = ["alpn"] +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[dev-dependencies.tempfile] +version = "3.0" + +[dev-dependencies.test-cert-gen] +version = "0.7" + +[features] +alpn = ["security-framework/alpn"] +vendored = ["openssl/vendored"] + +[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.lazy_static] +version = "1.4.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 = "2.0.0" + +[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.security-framework-sys] +version = "2.0.0" + +[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.tempfile] +version = "3.1.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.29" + +[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.55" + +[target."cfg(target_os = \"windows\")".dependencies.schannel] +version = "0.1.17" diff --git a/vendor/native-tls/LICENSE-APACHE b/vendor/native-tls/LICENSE-APACHE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/vendor/native-tls/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/native-tls/LICENSE-MIT b/vendor/native-tls/LICENSE-MIT new file mode 100644 index 000000000..981a42dc9 --- /dev/null +++ b/vendor/native-tls/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (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. diff --git a/vendor/native-tls/README.md b/vendor/native-tls/README.md new file mode 100644 index 000000000..fe5d04b84 --- /dev/null +++ b/vendor/native-tls/README.md @@ -0,0 +1,95 @@ +# 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 macOS (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,ignore +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,ignore +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<TcpStream>) { + // ... + } + + 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. diff --git a/vendor/native-tls/build.rs b/vendor/native-tls/build.rs new file mode 100644 index 000000000..cbac306a5 --- /dev/null +++ b/vendor/native-tls/build.rs @@ -0,0 +1,19 @@ +use 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"); + } + } +} diff --git a/vendor/native-tls/examples/google-connect.rs b/vendor/native-tls/examples/google-connect.rs new file mode 100644 index 000000000..bcf454245 --- /dev/null +++ b/vendor/native-tls/examples/google-connect.rs @@ -0,0 +1,17 @@ +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)); +} diff --git a/vendor/native-tls/examples/simple-server-pkcs8.rs b/vendor/native-tls/examples/simple-server-pkcs8.rs new file mode 100644 index 000000000..df9c95da0 --- /dev/null +++ b/vendor/native-tls/examples/simple-server-pkcs8.rs @@ -0,0 +1,45 @@ +extern crate native_tls; + +use native_tls::{Identity, TlsAcceptor, TlsStream}; +use std::fs::File; +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::sync::Arc; +use std::thread; + +fn main() { + let mut cert_file = File::open("test/cert.pem").unwrap(); + let mut certs = vec![]; + cert_file.read_to_end(&mut certs).unwrap(); + let mut key_file = File::open("test/key.pem").unwrap(); + let mut key = vec![]; + key_file.read_to_end(&mut key).unwrap(); + let pkcs8 = Identity::from_pkcs8(&certs, &key).unwrap(); + + let acceptor = TlsAcceptor::new(pkcs8).unwrap(); + let acceptor = Arc::new(acceptor); + + let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); + + fn handle_client(mut stream: TlsStream<TcpStream>) { + let mut buf = [0; 1024]; + let read = stream.read(&mut buf).unwrap(); + let received = std::str::from_utf8(&buf[0..read]).unwrap(); + stream + .write_all(format!("received '{}'", received).as_bytes()) + .unwrap(); + } + + 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 */ } + } + } +} diff --git a/vendor/native-tls/examples/simple-server.rs b/vendor/native-tls/examples/simple-server.rs new file mode 100644 index 000000000..483be948c --- /dev/null +++ b/vendor/native-tls/examples/simple-server.rs @@ -0,0 +1,37 @@ +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 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<TcpStream>) { + // ... + } + + 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 */ } + } + } +} diff --git a/vendor/native-tls/src/imp/openssl.rs b/vendor/native-tls/src/imp/openssl.rs new file mode 100644 index 000000000..389caa5e5 --- /dev/null +++ b/vendor/native-tls/src/imp/openssl.rs @@ -0,0 +1,483 @@ +extern 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::Pkcs12; +use self::openssl::pkey::{PKey, Private}; +use self::openssl::ssl::{ + self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod, + SslVerifyMode, +}; +use self::openssl::x509::{store::X509StoreBuilder, X509VerifyResult, X509}; +use std::error; +use std::fmt; +use std::io; +use std::sync::Once; + +use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder}; + +#[cfg(have_min_max_version)] +fn supported_protocols( + min: Option<Protocol>, + max: Option<Protocol>, + 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<Protocol>, + max: Option<Protocol>, + 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::new(); + 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), + EmptyChain, + NotPkcs8, +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Error::Normal(ref e) => error::Error::source(e), + Error::Ssl(ref e, _) => error::Error::source(e), + Error::EmptyChain => None, + Error::NotPkcs8 => None, + } + } +} + +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), + Error::EmptyChain => write!( + fmt, + "at least one certificate must be provided to create an identity" + ), + Error::NotPkcs8 => write!(fmt, "expected PKCS#8 PEM"), + } + } +} + +impl From<ErrorStack> for Error { + fn from(err: ErrorStack) -> Error { + Error::Normal(err) + } +} + +#[derive(Clone)] +pub struct Identity { + pkey: PKey<Private>, + cert: X509, + chain: Vec<X509>, +} + +impl Identity { + pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> { + let pkcs12 = Pkcs12::from_der(buf)?; + let parsed = pkcs12.parse(pass)?; + Ok(Identity { + pkey: parsed.pkey, + cert: parsed.cert, + // > The stack is the reverse of what you might expect due to the way + // > PKCS12_parse is implemented, so we need to load it backwards. + // > https://github.com/sfackler/rust-native-tls/commit/05fb5e583be589ab63d9f83d986d095639f8ec44 + chain: parsed.chain.into_iter().flatten().rev().collect(), + }) + } + + pub fn from_pkcs8(buf: &[u8], key: &[u8]) -> Result<Identity, Error> { + if !key.starts_with(b"-----BEGIN PRIVATE KEY-----") { + return Err(Error::NotPkcs8); + } + + let pkey = PKey::private_key_from_pem(key)?; + let mut cert_chain = X509::stack_from_pem(buf)?.into_iter(); + let cert = cert_chain.next().ok_or(Error::EmptyChain)?; + let chain = cert_chain.collect(); + Ok(Identity { pkey, cert, chain }) + } +} + +#[derive(Clone)] +pub struct Certificate(X509); + +impl Certificate { + pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> { + let cert = X509::from_der(buf)?; + Ok(Certificate(cert)) + } + + pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> { + let cert = X509::from_pem(buf)?; + Ok(Certificate(cert)) + } + + pub fn to_der(&self) -> Result<Vec<u8>, Error> { + let der = self.0.to_der()?; + Ok(der) + } +} + +pub struct MidHandshakeTlsStream<S>(MidHandshakeSslStream<S>); + +impl<S> fmt::Debug for MidHandshakeTlsStream<S> +where + S: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> MidHandshakeTlsStream<S> { + pub fn get_ref(&self) -> &S { + self.0.get_ref() + } + + pub fn get_mut(&mut self) -> &mut S { + self.0.get_mut() + } +} + +impl<S> MidHandshakeTlsStream<S> +where + S: io::Read + io::Write, +{ + pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> { + match self.0.handshake() { + Ok(s) => Ok(TlsStream(s)), + Err(e) => Err(e.into()), + } + } +} + +pub enum HandshakeError<S> { + Failure(Error), + WouldBlock(MidHandshakeTlsStream<S>), +} + +impl<S> From<ssl::HandshakeError<S>> for HandshakeError<S> { + fn from(e: ssl::HandshakeError<S>) -> HandshakeError<S> { + 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<S> From<ErrorStack> for HandshakeError<S> { + fn from(e: ErrorStack) -> HandshakeError<S> { + 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<TlsConnector, Error> { + init_trust(); + + let mut connector = SslConnector::builder(SslMethod::tls())?; + if let Some(ref identity) = builder.identity { + connector.set_certificate(&identity.0.cert)?; + connector.set_private_key(&identity.0.pkey)?; + for cert in identity.0.chain.iter() { + // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html + // specifies that "When sending a certificate chain, extra chain certificates are + // sent in order following the end entity certificate." + connector.add_extra_chain_cert(cert.to_owned())?; + } + } + supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?; + + if builder.disable_built_in_roots { + connector.set_cert_store(X509StoreBuilder::new()?.build()); + } + + 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(feature = "alpn")] + { + if !builder.alpn.is_empty() { + // Wire format is each alpn preceded by its length as a byte. + let mut alpn_wire_format = Vec::with_capacity( + builder + .alpn + .iter() + .map(|s| s.as_bytes().len()) + .sum::<usize>() + + builder.alpn.len(), + ); + for alpn in builder.alpn.iter().map(|s| s.as_bytes()) { + alpn_wire_format.push(alpn.len() as u8); + alpn_wire_format.extend(alpn); + } + connector.set_alpn_protos(&alpn_wire_format)?; + } + } + + #[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<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + 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)) + } +} + +impl fmt::Debug for TlsConnector { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TlsConnector") + // n.b. SslConnector is a newtype on SslContext which implements a noop Debug so it's omitted + .field("use_sni", &self.use_sni) + .field("accept_invalid_hostnames", &self.accept_invalid_hostnames) + .field("accept_invalid_certs", &self.accept_invalid_certs) + .finish() + } +} + +#[derive(Clone)] +pub struct TlsAcceptor(SslAcceptor); + +impl TlsAcceptor { + pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; + acceptor.set_private_key(&builder.identity.0.pkey)?; + acceptor.set_certificate(&builder.identity.0.cert)?; + for cert in builder.identity.0.chain.iter() { + // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html + // specifies that "When sending a certificate chain, extra chain certificates are + // sent in order following the end entity certificate." + 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<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + where + S: io::Read + io::Write, + { + let s = self.0.accept(stream)?; + Ok(TlsStream(s)) + } +} + +pub struct TlsStream<S>(ssl::SslStream<S>); + +impl<S: fmt::Debug> fmt::Debug for TlsStream<S> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> TlsStream<S> { + pub fn get_ref(&self) -> &S { + self.0.get_ref() + } + + pub fn get_mut(&mut self) -> &mut S { + self.0.get_mut() + } +} + +impl<S: io::Read + io::Write> TlsStream<S> { + pub fn buffered_read_size(&self) -> Result<usize, Error> { + Ok(self.0.ssl().pending()) + } + + pub fn peer_certificate(&self) -> Result<Option<Certificate>, Error> { + Ok(self.0.ssl().peer_certificate().map(Certificate)) + } + + #[cfg(feature = "alpn")] + pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>, Error> { + Ok(self + .0 + .ssl() + .selected_alpn_protocol() + .map(|alpn| alpn.to_vec())) + } + + pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, 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<S: io::Read + io::Write> io::Read for TlsStream<S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } +} + +impl<S: io::Read + io::Write> io::Write for TlsStream<S> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } +} diff --git a/vendor/native-tls/src/imp/schannel.rs b/vendor/native-tls/src/imp/schannel.rs new file mode 100644 index 000000000..62e5042f0 --- /dev/null +++ b/vendor/native-tls/src/imp/schannel.rs @@ -0,0 +1,562 @@ +extern crate schannel; + +use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec}; +use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions}; +use self::schannel::crypt_prov::{AcquireOptions, ProviderType}; +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 source(&self) -> Option<&(dyn error::Error + 'static)> { + error::Error::source(&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<io::Error> for Error { + fn from(error: io::Error) -> Error { + Error(error) + } +} + +#[derive(Clone)] +pub struct Identity { + cert: CertContext, +} + +impl Identity { + pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> { + 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 }) + } + + pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> { + if !key.starts_with(b"-----BEGIN PRIVATE KEY-----") { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "not a PKCS#8 key").into()); + } + + let mut store = Memory::new()?.into_store(); + let mut cert_iter = pem::PemBlock::new(pem).into_iter(); + let leaf = cert_iter.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "at least one certificate must be provided to create an identity", + ) + })?; + let cert = CertContext::from_pem(std::str::from_utf8(leaf).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "leaf cert contains invalid utf8", + ) + })?)?; + + let name = gen_container_name(); + let mut options = AcquireOptions::new(); + options.container(&name); + let type_ = ProviderType::rsa_full(); + + let mut container = match options.acquire(type_) { + Ok(container) => container, + Err(_) => options.new_keyset(true).acquire(type_)?, + }; + container.import().import_pkcs8_pem(&key)?; + + cert.set_key_prov_info() + .container(&name) + .type_(type_) + .keep_open(true) + .key_spec(KeySpec::key_exchange()) + .set()?; + let mut context = store.add_cert(&cert, CertAdd::Always)?; + + for int_cert in cert_iter { + let certificate = Certificate::from_pem(int_cert)?; + context = store.add_cert(&certificate.0, CertAdd::Always)?; + } + Ok(Identity { cert: context }) + } +} + +// The name of the container must be unique to have multiple active keys. +fn gen_container_name() -> String { + use std::sync::atomic::{AtomicUsize, Ordering}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + format!("native-tls-{}", COUNTER.fetch_add(1, Ordering::Relaxed)) +} + +#[derive(Clone)] +pub struct Certificate(CertContext); + +impl Certificate { + pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> { + let cert = CertContext::new(buf)?; + Ok(Certificate(cert)) + } + + pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> { + 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<Vec<u8>, Error> { + Ok(self.0.to_der().to_vec()) + } +} + +pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>); + +impl<S> fmt::Debug for MidHandshakeTlsStream<S> +where + S: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> MidHandshakeTlsStream<S> { + pub fn get_ref(&self) -> &S { + self.0.get_ref() + } + + pub fn get_mut(&mut self) -> &mut S { + self.0.get_mut() + } +} + +impl<S> MidHandshakeTlsStream<S> +where + S: io::Read + io::Write, +{ + pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> { + match self.0.handshake() { + Ok(s) => Ok(TlsStream(s)), + Err(e) => Err(e.into()), + } + } +} + +pub enum HandshakeError<S> { + Failure(Error), + WouldBlock(MidHandshakeTlsStream<S>), +} + +impl<S> From<tls_stream::HandshakeError<S>> for HandshakeError<S> { + fn from(e: tls_stream::HandshakeError<S>) -> HandshakeError<S> { + match e { + tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()), + tls_stream::HandshakeError::Interrupted(s) => { + HandshakeError::WouldBlock(MidHandshakeTlsStream(s)) + } + } + } +} + +impl<S> From<io::Error> for HandshakeError<S> { + fn from(e: io::Error) -> HandshakeError<S> { + HandshakeError::Failure(e.into()) + } +} + +#[derive(Clone, Debug)] +pub struct TlsConnector { + cert: Option<CertContext>, + roots: CertStore, + min_protocol: Option<::Protocol>, + max_protocol: Option<::Protocol>, + use_sni: bool, + accept_invalid_hostnames: bool, + accept_invalid_certs: bool, + disable_built_in_roots: bool, + #[cfg(feature = "alpn")] + alpn: Vec<String>, +} + +impl TlsConnector { + pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> { + 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, + disable_built_in_roots: builder.disable_built_in_roots, + #[cfg(feature = "alpn")] + alpn: builder.alpn.clone(), + }) + } + + pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + 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(())); + } else if self.disable_built_in_roots { + let roots_copy = self.roots.clone(); + builder.verify_callback(move |res| { + if let Err(err) = res.result() { + // Propagate previous error encountered during normal cert validation. + return Err(err); + } + + if let Some(chain) = res.chain() { + if chain + .certificates() + .any(|cert| roots_copy.certs().any(|root_cert| root_cert == cert)) + { + return Ok(()); + } + } + + Err(io::Error::new( + io::ErrorKind::Other, + "unable to find any user-specified roots in the final cert chain", + )) + }); + } + #[cfg(feature = "alpn")] + { + if !self.alpn.is_empty() { + builder.request_application_protocols( + &self.alpn.iter().map(|s| s.as_bytes()).collect::<Vec<_>>(), + ); + } + } + 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<TlsAcceptor, Error> { + Ok(TlsAcceptor { + cert: builder.identity.0.cert.clone(), + min_protocol: builder.min_protocol, + max_protocol: builder.max_protocol, + }) + } + + pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + 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<S>(tls_stream::TlsStream<S>); + +impl<S: fmt::Debug> fmt::Debug for TlsStream<S> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> TlsStream<S> { + pub fn get_ref(&self) -> &S { + self.0.get_ref() + } + + pub fn get_mut(&mut self) -> &mut S { + self.0.get_mut() + } +} + +impl<S: io::Read + io::Write> TlsStream<S> { + pub fn buffered_read_size(&self) -> Result<usize, Error> { + Ok(self.0.get_buf().len()) + } + + pub fn peer_certificate(&self) -> Result<Option<Certificate>, 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)), + } + } + + #[cfg(feature = "alpn")] + pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>, Error> { + Ok(self.0.negotiated_application_protocol()?) + } + + pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, 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<S: io::Read + io::Write> io::Read for TlsStream<S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } +} + +impl<S: io::Read + io::Write> io::Write for TlsStream<S> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } +} + +mod pem { + /// Split data by PEM guard lines + pub struct PemBlock<'a> { + pem_block: &'a str, + cur_end: usize, + } + + impl<'a> PemBlock<'a> { + pub fn new(data: &'a [u8]) -> PemBlock<'a> { + let s = ::std::str::from_utf8(data).unwrap(); + PemBlock { + pem_block: s, + cur_end: s.find("-----BEGIN").unwrap_or(s.len()), + } + } + } + + impl<'a> Iterator for PemBlock<'a> { + type Item = &'a [u8]; + fn next(&mut self) -> Option<Self::Item> { + let last = self.pem_block.len(); + if self.cur_end >= last { + return None; + } + let begin = self.cur_end; + let pos = self.pem_block[begin + 1..].find("-----BEGIN"); + self.cur_end = match pos { + Some(end) => end + begin + 1, + None => last, + }; + return Some(&self.pem_block[begin..self.cur_end].as_bytes()); + } + } + + #[test] + fn test_split() { + // Split three certs, CRLF line terminators. + assert_eq!( + PemBlock::new( + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n\ + -----BEGIN SECOND-----\r\n-----END SECOND\r\n\ + -----BEGIN THIRD-----\r\n-----END THIRD\r\n" + ) + .collect::<Vec<&[u8]>>(), + vec![ + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n" as &[u8], + b"-----BEGIN SECOND-----\r\n-----END SECOND\r\n", + b"-----BEGIN THIRD-----\r\n-----END THIRD\r\n" + ] + ); + // Split three certs, CRLF line terminators except at EOF. + assert_eq!( + PemBlock::new( + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n\ + -----BEGIN SECOND-----\r\n-----END SECOND-----\r\n\ + -----BEGIN THIRD-----\r\n-----END THIRD-----" + ) + .collect::<Vec<&[u8]>>(), + vec![ + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n" as &[u8], + b"-----BEGIN SECOND-----\r\n-----END SECOND-----\r\n", + b"-----BEGIN THIRD-----\r\n-----END THIRD-----" + ] + ); + // Split two certs, LF line terminators. + assert_eq!( + PemBlock::new( + b"-----BEGIN FIRST-----\n-----END FIRST-----\n\ + -----BEGIN SECOND-----\n-----END SECOND\n" + ) + .collect::<Vec<&[u8]>>(), + vec![ + b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8], + b"-----BEGIN SECOND-----\n-----END SECOND\n" + ] + ); + // Split two certs, CR line terminators. + assert_eq!( + PemBlock::new( + b"-----BEGIN FIRST-----\r-----END FIRST-----\r\ + -----BEGIN SECOND-----\r-----END SECOND\r" + ) + .collect::<Vec<&[u8]>>(), + vec![ + b"-----BEGIN FIRST-----\r-----END FIRST-----\r" as &[u8], + b"-----BEGIN SECOND-----\r-----END SECOND\r" + ] + ); + // Split two certs, LF line terminators except at EOF. + assert_eq!( + PemBlock::new( + b"-----BEGIN FIRST-----\n-----END FIRST-----\n\ + -----BEGIN SECOND-----\n-----END SECOND" + ) + .collect::<Vec<&[u8]>>(), + vec![ + b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8], + b"-----BEGIN SECOND-----\n-----END SECOND" + ] + ); + // Split a single cert, LF line terminators. + assert_eq!( + PemBlock::new(b"-----BEGIN FIRST-----\n-----END FIRST-----\n").collect::<Vec<&[u8]>>(), + vec![b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8]] + ); + // Split a single cert, LF line terminators except at EOF. + assert_eq!( + PemBlock::new(b"-----BEGIN FIRST-----\n-----END FIRST-----").collect::<Vec<&[u8]>>(), + vec![b"-----BEGIN FIRST-----\n-----END FIRST-----" as &[u8]] + ); + // (Don't) split garbage. + assert_eq!( + PemBlock::new(b"junk").collect::<Vec<&[u8]>>(), + Vec::<&[u8]>::new() + ); + assert_eq!( + PemBlock::new(b"junk-----BEGIN garbage").collect::<Vec<&[u8]>>(), + vec![b"-----BEGIN garbage" as &[u8]] + ); + } +} diff --git a/vendor/native-tls/src/imp/security_framework.rs b/vendor/native-tls/src/imp/security_framework.rs new file mode 100644 index 000000000..0b4177228 --- /dev/null +++ b/vendor/native-tls/src/imp/security_framework.rs @@ -0,0 +1,632 @@ +extern 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::random::SecRandom; +use self::security_framework::secure_transport::{ + self, ClientBuilder, SslConnectionType, SslContext, SslProtocol, SslProtocolSide, +}; +use self::security_framework_sys::base::{errSecIO, errSecParam}; +use self::tempfile::TempDir; +use std::error; +use std::fmt; +use std::io; +use std::str; +use std::sync::Mutex; +use std::sync::Once; + +#[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::identity::SecIdentityExt; +#[cfg(not(target_os = "ios"))] +use self::security_framework::os::macos::import_export::{ + ImportOptions, Pkcs12ImportOptionsExt, SecItems, +}; +#[cfg(not(target_os = "ios"))] +use self::security_framework::os::macos::keychain::{self, KeychainSettings, SecKeychain}; + +use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder}; + +static SET_AT_EXIT: Once = Once::new(); + +#[cfg(not(target_os = "ios"))] +lazy_static! { + static ref TEMP_KEYCHAIN: Mutex<Option<(SecKeychain, TempDir)>> = 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 source(&self) -> Option<&(dyn error::Error + 'static)> { + error::Error::source(&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<base::Error> for Error { + fn from(error: base::Error) -> Error { + Error(error) + } +} + +#[derive(Clone, Debug)] +pub struct Identity { + identity: SecIdentity, + chain: Vec<SecCertificate>, +} + +impl Identity { + #[cfg(target_os = "ios")] + pub fn from_pkcs8(_: &[u8], _: &[u8]) -> Result<Identity, Error> { + panic!("Not implemented on iOS"); + } + + #[cfg(not(target_os = "ios"))] + pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> { + if !key.starts_with(b"-----BEGIN PRIVATE KEY-----") { + return Err(Error(base::Error::from(errSecParam))); + } + + let dir = TempDir::new().map_err(|_| Error(base::Error::from(errSecIO)))?; + let keychain = keychain::CreateOptions::new() + .password(&random_password()?) + .create(dir.path().join("identity.keychain"))?; + + let mut items = SecItems::default(); + + ImportOptions::new() + .filename("key.pem") + .items(&mut items) + .keychain(&keychain) + .import(&key)?; + + ImportOptions::new() + .filename("chain.pem") + .items(&mut items) + .keychain(&keychain) + .import(&pem)?; + + let cert = items + .certificates + .get(0) + .ok_or_else(|| Error(base::Error::from(errSecParam)))?; + let ident = SecIdentity::with_certificate(&[keychain], cert)?; + Ok(Identity { + identity: ident, + chain: items.certificates, + }) + } + + pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> { + 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, + 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<Vec<ImportedIdentity>, 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 mut import_opts = Pkcs12ImportOptions::new(); + // Method shadowed by deprecated method. + <Pkcs12ImportOptions as Pkcs12ImportOptionsExt>::keychain(&mut import_opts, keychain); + let imports = import_opts.passphrase(pass).import(buf)?; + Ok(imports) + } + + #[cfg(target_os = "ios")] + fn import_options(buf: &[u8], pass: &str) -> Result<Vec<ImportedIdentity>, Error> { + let imports = Pkcs12ImportOptions::new().passphrase(pass).import(buf)?; + Ok(imports) + } +} + +fn random_password() -> Result<String, Error> { + use std::fmt::Write; + let mut bytes = [0_u8; 10]; + SecRandom::default() + .copy_bytes(&mut bytes) + .map_err(|_| Error(base::Error::from(errSecIO)))?; + let mut s = String::with_capacity(2 * bytes.len()); + for byte in bytes { + write!(s, "{:02X}", byte).map_err(|_| Error(base::Error::from(errSecIO)))?; + } + Ok(s) +} + +#[derive(Clone)] +pub struct Certificate(SecCertificate); + +impl Certificate { + pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> { + let cert = SecCertificate::from_der(buf)?; + Ok(Certificate(cert)) + } + + #[cfg(not(target_os = "ios"))] + pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> { + 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(_: &[u8]) -> Result<Certificate, Error> { + panic!("Not implemented on iOS"); + } + + pub fn to_der(&self) -> Result<Vec<u8>, Error> { + Ok(self.0.to_der()) + } +} + +pub enum HandshakeError<S> { + WouldBlock(MidHandshakeTlsStream<S>), + Failure(Error), +} + +impl<S> From<secure_transport::ClientHandshakeError<S>> for HandshakeError<S> { + fn from(e: secure_transport::ClientHandshakeError<S>) -> HandshakeError<S> { + match e { + secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()), + secure_transport::ClientHandshakeError::Interrupted(s) => { + HandshakeError::WouldBlock(MidHandshakeTlsStream::Client(s)) + } + } + } +} + +impl<S> From<base::Error> for HandshakeError<S> { + fn from(e: base::Error) -> HandshakeError<S> { + HandshakeError::Failure(e.into()) + } +} + +pub enum MidHandshakeTlsStream<S> { + Server( + secure_transport::MidHandshakeSslStream<S>, + Option<SecCertificate>, + ), + Client(secure_transport::MidHandshakeClientBuilder<S>), +} + +impl<S> fmt::Debug for MidHandshakeTlsStream<S> +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<S> MidHandshakeTlsStream<S> { + 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(), + } + } +} + +impl<S> MidHandshakeTlsStream<S> +where + S: io::Read + io::Write, +{ + pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> { + 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, Debug)] +pub struct TlsConnector { + identity: Option<Identity>, + min_protocol: Option<Protocol>, + max_protocol: Option<Protocol>, + roots: Vec<SecCertificate>, + use_sni: bool, + danger_accept_invalid_hostnames: bool, + danger_accept_invalid_certs: bool, + disable_built_in_roots: bool, + #[cfg(feature = "alpn")] + alpn: Vec<String>, +} + +impl TlsConnector { + pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> { + 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, + disable_built_in_roots: builder.disable_built_in_roots, + #[cfg(feature = "alpn")] + alpn: builder.alpn.clone(), + }) + } + + pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + 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); + builder.trust_anchor_certificates_only(self.disable_built_in_roots); + + #[cfg(feature = "alpn")] + { + if !self.alpn.is_empty() { + builder.alpn_protocols(&self.alpn.iter().map(String::as_str).collect::<Vec<_>>()); + } + } + + 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<Protocol>, + max_protocol: Option<Protocol>, +} + +impl TlsAcceptor { + pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> { + Ok(TlsAcceptor { + identity: builder.identity.0.clone(), + min_protocol: builder.min_protocol, + max_protocol: builder.max_protocol, + }) + } + + pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>> + 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<S> { + stream: secure_transport::SslStream<S>, + cert: Option<SecCertificate>, +} + +impl<S: fmt::Debug> fmt::Debug for TlsStream<S> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.stream, fmt) + } +} + +impl<S> TlsStream<S> { + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } +} + +impl<S: io::Read + io::Write> TlsStream<S> { + pub fn buffered_read_size(&self) -> Result<usize, Error> { + Ok(self.stream.context().buffered_read_size()?) + } + + #[allow(deprecated)] + pub fn peer_certificate(&self) -> Result<Option<Certificate>, 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(feature = "alpn")] + pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>, Error> { + match self.stream.context().alpn_protocols() { + Ok(protocols) => { + // Per RFC7301, "ProtocolNameList" MUST contain exactly one "ProtocolName". + assert!(protocols.len() < 2); + + if protocols.is_empty() { + // Not sure this is actually possible. + Ok(None) + } else { + Ok(Some(protocols.into_iter().next().unwrap().into_bytes())) + } + } + // The macOS API appears to return `errSecParam` whenever no ALPN was negotiated, both + // when it isn't attempted and when it isn't successful. + Err(e) if e.code() == errSecParam => Ok(None), + Err(other) => Err(Error::from(other)), + } + } + + #[cfg(target_os = "ios")] + pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, Error> { + Ok(None) + } + + #[cfg(not(target_os = "ios"))] + pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, 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<S: io::Read + io::Write> io::Read for TlsStream<S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.stream.read(buf) + } +} + +impl<S: io::Read + io::Write> io::Write for TlsStream<S> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + 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<u8> { + 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; +} diff --git a/vendor/native-tls/src/lib.rs b/vendor/native-tls/src/lib.rs new file mode 100644 index 000000000..8b7415394 --- /dev/null +++ b/vendor/native-tls/src/lib.rs @@ -0,0 +1,721 @@ +//! 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 features 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 +//! * X.509/PKCS#8 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<TcpStream>) { +//! // ... +//! } +//! +//! 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)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[macro_use] +#[cfg(any(target_os = "macos", target_os = "ios"))] +extern crate lazy_static; + +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<T> = result::Result<T, Error>; + +/// An error returned from the TLS implementation. +pub struct Error(imp::Error); + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + error::Error::source(&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<imp::Error> 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. +#[derive(Clone)] +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<Identity> { + let identity = imp::Identity::from_pkcs12(der, password)?; + Ok(Identity(identity)) + } + + /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first. + /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate. + /// + /// The certificate chain should contain any intermediate cerficates that should be sent to + /// clients to allow them to build a chain to a trusted root. + /// + /// A certificate chain here means a series of PEM encoded certificates concatenated together. + pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity> { + let identity = imp::Identity::from_pkcs8(pem, key)?; + 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<Certificate> { + let cert = imp::Certificate::from_der(der)?; + Ok(Certificate(cert)) + } + + /// Parses a PEM-formatted X509 certificate. + pub fn from_pem(pem: &[u8]) -> Result<Certificate> { + let cert = imp::Certificate::from_pem(pem)?; + Ok(Certificate(cert)) + } + + /// Returns the DER-encoded representation of this certificate. + pub fn to_der(&self) -> Result<Vec<u8>> { + let der = self.0.to_der()?; + Ok(der) + } +} + +/// A TLS stream which has been interrupted midway through the handshake process. +pub struct MidHandshakeTlsStream<S>(imp::MidHandshakeTlsStream<S>); + +impl<S> fmt::Debug for MidHandshakeTlsStream<S> +where + S: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> MidHandshakeTlsStream<S> { + /// 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() + } +} + +impl<S> MidHandshakeTlsStream<S> +where + S: io::Read + io::Write, +{ + /// 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<TlsStream<S>, HandshakeError<S>> { + 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<S> { + /// 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<S>), +} + +impl<S> error::Error for HandshakeError<S> +where + S: Any + fmt::Debug, +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + HandshakeError::Failure(ref e) => Some(e), + HandshakeError::WouldBlock(_) => None, + } + } +} + +impl<S> fmt::Display for HandshakeError<S> +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<S> From<imp::HandshakeError<S>> for HandshakeError<S> { + fn from(e: imp::HandshakeError<S>) -> HandshakeError<S> { + 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<Identity>, + min_protocol: Option<Protocol>, + max_protocol: Option<Protocol>, + root_certificates: Vec<Certificate>, + accept_invalid_certs: bool, + accept_invalid_hostnames: bool, + use_sni: bool, + disable_built_in_roots: bool, + #[cfg(feature = "alpn")] + alpn: Vec<String>, +} + +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<Protocol>) -> &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<Protocol>) -> &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 built-in system certificates during certificate validation. + /// + /// Defaults to `false` -- built-in system certs will be used. + pub fn disable_built_in_roots(&mut self, disable: bool) -> &mut TlsConnectorBuilder { + self.disable_built_in_roots = disable; + self + } + + /// Request specific protocols through ALPN (Application-Layer Protocol Negotiation). + /// + /// Defaults to no protocols. + #[cfg(feature = "alpn")] + #[cfg_attr(docsrs, doc(cfg(feature = "alpn")))] + pub fn request_alpns(&mut self, protocols: &[&str]) -> &mut TlsConnectorBuilder { + self.alpn = protocols.iter().map(|s| (*s).to_owned()).collect(); + 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<TlsConnector> { + 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, Debug)] +pub struct TlsConnector(imp::TlsConnector); + +impl TlsConnector { + /// Returns a new connector with default settings. + pub fn new() -> Result<TlsConnector> { + 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, + disable_built_in_roots: false, + #[cfg(feature = "alpn")] + alpn: vec![], + } + } + + /// 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<S>( + &self, + domain: &str, + stream: S, + ) -> result::Result<TlsStream<S>, HandshakeError<S>> + 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<Protocol>, + max_protocol: Option<Protocol>, +} + +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<Protocol>) -> &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<Protocol>) -> &mut TlsAcceptorBuilder { + self.max_protocol = protocol; + self + } + + /// Creates a new `TlsAcceptor`. + pub fn build(&self) -> Result<TlsAcceptor> { + 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<TcpStream>) { +/// // ... +/// } +/// +/// 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> { + 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<S>(&self, stream: S) -> result::Result<TlsStream<S>, HandshakeError<S>> + 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<S>(imp::TlsStream<S>); + +impl<S: fmt::Debug> fmt::Debug for TlsStream<S> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, fmt) + } +} + +impl<S> TlsStream<S> { + /// 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() + } +} + +impl<S: io::Read + io::Write> TlsStream<S> { + /// Returns the number of bytes that can be read without resulting in any + /// network calls. + pub fn buffered_read_size(&self) -> Result<usize> { + Ok(self.0.buffered_read_size()?) + } + + /// Returns the peer's leaf certificate, if available. + pub fn peer_certificate(&self) -> Result<Option<Certificate>> { + 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<Option<Vec<u8>>> { + Ok(self.0.tls_server_end_point()?) + } + + /// Returns the negotiated ALPN protocol. + #[cfg(feature = "alpn")] + #[cfg_attr(docsrs, doc(cfg(feature = "alpn")))] + pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>> { + Ok(self.0.negotiated_alpn()?) + } + + /// Shuts down the TLS session. + pub fn shutdown(&mut self) -> io::Result<()> { + self.0.shutdown()?; + Ok(()) + } +} + +impl<S: io::Read + io::Write> io::Read for TlsStream<S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } +} + +impl<S: io::Read + io::Write> io::Write for TlsStream<S> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } +} + +fn _check_kinds() { + use std::net::TcpStream; + + fn is_sync<T: Sync>() {} + fn is_send<T: Send>() {} + is_sync::<Error>(); + is_send::<Error>(); + is_sync::<TlsConnectorBuilder>(); + is_send::<TlsConnectorBuilder>(); + is_sync::<TlsConnector>(); + is_send::<TlsConnector>(); + is_sync::<TlsAcceptorBuilder>(); + is_send::<TlsAcceptorBuilder>(); + is_sync::<TlsAcceptor>(); + is_send::<TlsAcceptor>(); + is_sync::<TlsStream<TcpStream>>(); + is_send::<TlsStream<TcpStream>>(); + is_sync::<MidHandshakeTlsStream<TcpStream>>(); + is_send::<MidHandshakeTlsStream<TcpStream>>(); +} diff --git a/vendor/native-tls/src/test.rs b/vendor/native-tls/src/test.rs new file mode 100644 index 000000000..d29f0d264 --- /dev/null +++ b/vendor/native-tls/src/test.rs @@ -0,0 +1,573 @@ +use std::fs; +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::process::{Command, Stdio}; +use std::string::String; +use std::thread; + +use super::*; + +macro_rules! p { + ($e:expr) => { + match $e { + Ok(r) => r, + Err(e) => panic!("{:?}", e), + } + }; +} + +#[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"</HTML>\r\n") || result.ends_with(b"</html>")); +} + +#[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 connect_no_root_certs() { + let builder = p!(TlsConnector::builder().disable_built_in_roots(true).build()); + let s = p!(TcpStream::connect("google.com:443")); + assert!(builder.connect("google.com", s).is_err()); +} + +#[test] +fn server_no_root_certs() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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 = Certificate::from_der(keys.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port))); + let builder = p!(TlsConnector::builder() + .disable_built_in_roots(true) + .add_root_certificate(root_ca) + .build()); + let mut socket = p!(builder.connect("localhost", 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() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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 = Certificate::from_der(keys.client.ca.get_der()).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("localhost", 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 certificate_from_pem() { + let dir = tempfile::tempdir().unwrap(); + let keys = test_cert_gen::keys(); + + let der_path = dir.path().join("cert.der"); + fs::write(&der_path, &keys.client.ca.get_der()).unwrap(); + let output = Command::new("openssl") + .arg("x509") + .arg("-in") + .arg(der_path) + .arg("-inform") + .arg("der") + .stderr(Stdio::piped()) + .output() + .unwrap(); + + assert!(output.status.success()); + + let cert = Certificate::from_pem(&output.stdout).unwrap(); + assert_eq!(cert.to_der().unwrap(), keys.client.ca.get_der()); +} + +#[test] +fn peer_certificate() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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 = Certificate::from_der(keys.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port))); + let builder = p!(TlsConnector::builder() + .add_root_certificate(root_ca) + .build()); + let socket = p!(builder.connect("localhost", socket)); + + let cert = socket.peer_certificate().unwrap().unwrap(); + assert_eq!( + cert.to_der().unwrap(), + keys.server.cert_and_key.cert.get_der() + ); + + p!(j.join()); +} + +#[test] +fn server_tls11_only() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + let builder = p!(TlsAcceptor::builder(identity) + .min_protocol_version(Some(Protocol::Tlsv12)) + .max_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; + 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 = Certificate::from_der(keys.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port))); + let builder = p!(TlsConnector::builder() + .add_root_certificate(root_ca) + .min_protocol_version(Some(Protocol::Tlsv12)) + .max_protocol_version(Some(Protocol::Tlsv12)) + .build()); + let mut socket = p!(builder.connect("localhost", 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 keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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 = Certificate::from_der(keys.client.ca.get_der()).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()); + assert!(builder.connect("localhost", socket).is_err()); + + p!(j.join()); +} + +#[test] +fn server_untrusted() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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("localhost", socket).unwrap_err(); + + p!(j.join()); +} + +#[test] +fn server_untrusted_unverified() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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("localhost", 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 keys = test_cert_gen::keys(); + + let _ = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + let _ = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + + let cert = keys.server.cert_and_key.cert.to_pem().into_bytes(); + let key = rsa_to_pkcs8(&keys.server.cert_and_key.key.to_pem_incorrect()).into_bytes(); + let _ = p!(Identity::from_pkcs8(&cert, &key)); + let _ = p!(Identity::from_pkcs8(&cert, &key)); +} + +#[test] +fn from_pkcs8_rejects_rsa_key() { + let keys = test_cert_gen::keys(); + let cert = keys.server.cert_and_key.cert.to_pem().into_bytes(); + let rsa_key = keys.server.cert_and_key.key.to_pem_incorrect(); + assert!(Identity::from_pkcs8(&cert, rsa_key.as_bytes()).is_err()); + let pkcs8_key = rsa_to_pkcs8(&rsa_key); + assert!(Identity::from_pkcs8(&cert, pkcs8_key.as_bytes()).is_ok()); +} + +#[test] +fn shutdown() { + let keys = test_cert_gen::keys(); + + let identity = p!(Identity::from_pkcs12( + &keys.server.cert_and_key_pkcs12.pkcs12.0, + &keys.server.cert_and_key_pkcs12.password + )); + 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 = Certificate::from_der(keys.client.ca.get_der()).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("localhost", socket)); + + p!(socket.write_all(b"hello")); + p!(socket.shutdown()); + + p!(j.join()); +} + +#[test] +#[cfg(feature = "alpn")] +fn alpn_google_h2() { + let builder = p!(TlsConnector::builder().request_alpns(&["h2"]).build()); + let s = p!(TcpStream::connect("google.com:443")); + let socket = p!(builder.connect("google.com", s)); + let alpn = p!(socket.negotiated_alpn()); + assert_eq!(alpn, Some(b"h2".to_vec())); +} + +#[test] +#[cfg(feature = "alpn")] +fn alpn_google_invalid() { + let builder = p!(TlsConnector::builder().request_alpns(&["h2c"]).build()); + let s = p!(TcpStream::connect("google.com:443")); + let socket = p!(builder.connect("google.com", s)); + let alpn = p!(socket.negotiated_alpn()); + assert_eq!(alpn, None); +} + +#[test] +#[cfg(feature = "alpn")] +fn alpn_google_none() { + let builder = p!(TlsConnector::new()); + let s = p!(TcpStream::connect("google.com:443")); + let socket = p!(builder.connect("google.com", s)); + let alpn = p!(socket.negotiated_alpn()); + assert_eq!(alpn, None); +} + +#[test] +fn server_pkcs8() { + let keys = test_cert_gen::keys(); + let cert = keys.server.cert_and_key.cert.to_pem().into_bytes(); + let key = rsa_to_pkcs8(&keys.server.cert_and_key.key.to_pem_incorrect()).into_bytes(); + + let ident = Identity::from_pkcs8(&cert, &key).unwrap(); + let ident2 = ident.clone(); + let builder = p!(TlsAcceptor::new(ident)); + + 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 = Certificate::from_der(keys.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port))); + let mut builder = TlsConnector::builder(); + // FIXME + // This checks that we can successfully add a certificate on the client side. + // Unfortunately, we can not request client certificates through the API of this library, + // otherwise we could check in the server thread that + // socket.peer_certificate().unwrap().is_some() + builder.identity(ident2); + + builder.add_root_certificate(root_ca); + let builder = p!(builder.build()); + let mut socket = p!(builder.connect("localhost", 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 two_servers() { + let keys1 = test_cert_gen::gen_keys(); + let cert = keys1.server.cert_and_key.cert.to_pem().into_bytes(); + let key = rsa_to_pkcs8(&keys1.server.cert_and_key.key.to_pem_incorrect()).into_bytes(); + let identity = p!(Identity::from_pkcs8(&cert, &key)); + let builder = TlsAcceptor::builder(identity); + let builder = p!(builder.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 keys2 = test_cert_gen::gen_keys(); + let cert = keys2.server.cert_and_key.cert.to_pem().into_bytes(); + let key = rsa_to_pkcs8(&keys2.server.cert_and_key.key.to_pem_incorrect()).into_bytes(); + let identity = p!(Identity::from_pkcs8(&cert, &key)); + let builder = TlsAcceptor::builder(identity); + let builder = p!(builder.build()); + + let listener = p!(TcpListener::bind("0.0.0.0:0")); + let port2 = p!(listener.local_addr()).port(); + + let j2 = 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 = Certificate::from_der(keys1.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port))); + let mut builder = TlsConnector::builder(); + builder.add_root_certificate(root_ca); + let builder = p!(builder.build()); + let mut socket = p!(builder.connect("localhost", socket)); + + p!(socket.write_all(b"hello")); + let mut buf = vec![]; + p!(socket.read_to_end(&mut buf)); + assert_eq!(buf, b"world"); + + let root_ca = Certificate::from_der(keys2.client.ca.get_der()).unwrap(); + + let socket = p!(TcpStream::connect(("localhost", port2))); + let mut builder = TlsConnector::builder(); + builder.add_root_certificate(root_ca); + let builder = p!(builder.build()); + let mut socket = p!(builder.connect("localhost", 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()); + p!(j2.join()); +} + +fn rsa_to_pkcs8(pem: &str) -> String { + let mut child = Command::new("openssl") + .arg("pkcs8") + .arg("-topk8") + .arg("-nocrypt") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + { + let child_stdin = child.stdin.as_mut().unwrap(); + child_stdin.write_all(pem.as_bytes()).unwrap(); + } + String::from_utf8(child.wait_with_output().unwrap().stdout).unwrap() +} |