Files
cli/vendor/rustls-native-certs/tests/smoketests.rs

330 lines
8.4 KiB
Rust

mod common;
use std::io::{ErrorKind, Read, Write};
use std::net::TcpStream;
#[cfg(unix)]
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{env, panic};
// #[serial] is used on all these tests to run them sequentially. If they're run in parallel,
// the global env var configuration in the env var test interferes with the others.
use serial_test::serial;
/// Check if connection to site works using native roots.
///
/// Yields an Err if and only if there is an issue connecting that
/// appears to be due to a certificate problem.
///
/// # Panics
///
/// Panics on errors unrelated to the TLS connection like errors during
/// certificate loading, or connecting via TCP.
fn check_site(domain: &str) -> Result<(), ()> {
check_site_with_roots(domain, rustls_native_certs::load_native_certs().unwrap())
}
/// Check if connection to site works using the given roots.
///
/// Yields an Err if and only if there is an issue connecting that
/// appears to be due to a certificate problem.
///
/// # Panics
///
/// Panics on errors unrelated to the TLS connection like connecting via TCP.
fn check_site_with_roots(
domain: &str,
root_certs: Vec<pki_types::CertificateDer<'static>>,
) -> Result<(), ()> {
let mut roots = rustls::RootCertStore::empty();
roots.add_parsable_certificates(root_certs);
let config = rustls::ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth();
let mut conn = rustls::ClientConnection::new(
Arc::new(config),
pki_types::ServerName::try_from(domain)
.unwrap()
.to_owned(),
)
.unwrap();
let mut sock = TcpStream::connect(format!("{domain}:443")).unwrap();
let mut tls = rustls::Stream::new(&mut conn, &mut sock);
let result = tls.write_all(
format!(
"GET / HTTP/1.1\r\n\
Host: {domain}\r\n\
Connection: close\r\n\
Accept-Encoding: identity\r\n\
\r\n"
)
.as_bytes(),
);
match result {
Ok(()) => (),
Err(e) if e.kind() == ErrorKind::InvalidData => return Err(()), // TLS error
Err(e) => panic!("{e}"),
}
let mut plaintext = [0u8; 1024];
let len = tls.read(&mut plaintext).unwrap();
assert!(plaintext[..len].starts_with(b"HTTP/1.1 ")); // or whatever
Ok(())
}
#[test]
#[serial]
#[ignore]
fn google() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("google.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn amazon() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("amazon.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn facebook() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("facebook.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn netflix() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("netflix.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn ebay() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("ebay.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn apple() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
check_site("apple.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn badssl_with_env() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
// Self-signed certs should never be trusted by default:
assert!(check_site("self-signed.badssl.com").is_err());
// But they should be trusted if SSL_CERT_FILE is set:
env::set_var(
"SSL_CERT_FILE",
// The CA cert, downloaded directly from the site itself:
PathBuf::from("./tests/badssl-com-chain.pem"),
);
check_site("self-signed.badssl.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn badssl_with_dir_from_env() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
let temp_dir = tempfile::TempDir::new().unwrap();
let original = Path::new("tests/badssl-com-chain.pem")
.canonicalize()
.unwrap();
let link1 = temp_dir.path().join("5d30f3c5.3");
#[cfg(unix)]
let link2 = temp_dir.path().join("fd3003c5.0");
env::set_var(
"SSL_CERT_DIR",
// The CA cert, downloaded directly from the site itself:
temp_dir.path(),
);
assert!(check_site("self-signed.badssl.com").is_err());
// OpenSSL uses symlinks too. So, use one for testing too, if possible.
#[cfg(unix)]
symlink(original, link1).unwrap();
#[cfg(not(unix))]
std::fs::copy(original, link1).unwrap();
// Dangling symlink
#[cfg(unix)]
symlink("/a/path/which/does/not/exist/hopefully", link2).unwrap();
check_site("self-signed.badssl.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn ssl_cert_dir_multiple_paths_are_respected() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
// Create 2 temporary directories
let temp_dir1 = tempfile::TempDir::new().unwrap();
let temp_dir2 = tempfile::TempDir::new().unwrap();
// Copy the certificate to the 2nd dir, leaving the 1st one
// empty.
let original = Path::new("tests/badssl-com-chain.pem")
.canonicalize()
.unwrap();
let cert = temp_dir2.path().join("5d30f3c5.3");
std::fs::copy(original, cert).unwrap();
let list_sep = if cfg!(windows) { ';' } else { ':' };
let value = format!(
"{}{}{}",
temp_dir1.path().display(),
list_sep,
temp_dir2.path().display()
);
env::set_var("SSL_CERT_DIR", value);
check_site("self-signed.badssl.com").unwrap();
}
#[test]
#[serial]
#[ignore]
fn ssl_cert_dir_non_hash_based_name() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
// Create temporary directory
let temp_dir = tempfile::TempDir::new().unwrap();
// Copy the certificate to the dir
let original = Path::new("tests/badssl-com-chain.pem")
.canonicalize()
.unwrap();
let cert = temp_dir.path().join("test.pem");
std::fs::copy(original, cert).unwrap();
env::set_var(
"SSL_CERT_DIR",
// The CA cert, downloaded directly from the site itself:
temp_dir.path(),
);
check_site("self-signed.badssl.com").unwrap();
}
#[test]
#[serial]
#[ignore]
#[cfg(target_os = "linux")]
fn google_with_dir_but_broken_file() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
env::set_var("SSL_CERT_DIR", "/etc/ssl/certs");
env::set_var("SSL_CERT_FILE", "not-exist");
let res = rustls_native_certs::load_native_certs();
let first_err = res.errors.first().unwrap().to_string();
dbg!(&first_err);
assert!(first_err.contains("from file"));
assert!(first_err.contains("not-exist"));
check_site_with_roots("google.com", res.certs).unwrap();
}
#[test]
#[serial]
#[ignore]
#[cfg(target_os = "linux")]
fn google_with_file_but_broken_dir() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
env::set_var("SSL_CERT_DIR", "/not-exist");
env::set_var("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt");
let res = rustls_native_certs::load_native_certs();
let first_err = res.errors.first().unwrap().to_string();
dbg!(&first_err);
assert!(first_err.contains("opening directory"));
assert!(first_err.contains("/not-exist"));
check_site_with_roots("google.com", res.certs).unwrap();
}
#[test]
#[serial]
#[ignore]
#[cfg(target_os = "linux")]
fn nothing_works_with_broken_file_and_dir() {
unsafe {
// SAFETY: safe because of #[serial]
common::clear_env();
}
env::set_var("SSL_CERT_DIR", "/not-exist");
env::set_var("SSL_CERT_FILE", "not-exist");
let res = rustls_native_certs::load_native_certs();
assert_eq!(res.errors.len(), 2);
let first_err = res.errors.first().unwrap().to_string();
dbg!(&first_err);
assert!(first_err.contains("from file"));
assert!(first_err.contains("not-exist"));
let second_err = res.errors.get(1).unwrap().to_string();
dbg!(&second_err);
assert!(second_err.contains("opening directory"));
assert!(second_err.contains("/not-exist"));
}