diff --git a/components/net/connector.rs b/components/net/connector.rs index 3e4a8071f28..8211e415f34 100644 --- a/components/net/connector.rs +++ b/components/net/connector.rs @@ -27,11 +27,11 @@ const DEFAULT_CIPHERS: &'static str = concat!( "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" ); -pub fn create_http_connector() -> Arc> { +pub fn create_http_connector(certificate_file: &str) -> Arc> { let mut context = SslContext::new(SslMethod::Sslv23).unwrap(); context.set_CA_file(&resources_dir_path() .expect("Need certificate file to make network requests") - .join("certs")).unwrap(); + .join(certificate_file)).unwrap(); context.set_cipher_list(DEFAULT_CIPHERS).unwrap(); context.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION); let connector = HttpsConnector::new(ServoSslClient { diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index de41bfa202a..0a284ee4016 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -189,7 +189,15 @@ pub fn main_fetch(request: Rc, } // Step 9 - // TODO this step (HSTS) + if !request.current_url().is_secure_scheme() && request.current_url().domain().is_some() { + if context.state + .hsts_list + .read() + .unwrap() + .is_host_secure(request.current_url().domain().unwrap()) { + request.url_list.borrow_mut().last_mut().unwrap().as_mut_url().unwrap().set_scheme("https").unwrap(); + } + } // Step 10 // this step is obsoleted by fetch_async diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 63e72db3ffa..be73b564350 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -76,13 +76,13 @@ pub struct HttpState { } impl HttpState { - pub fn new() -> HttpState { + pub fn new(certificate_path: &str) -> HttpState { HttpState { hsts_list: Arc::new(RwLock::new(HstsList::new())), cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))), auth_cache: Arc::new(RwLock::new(AuthCache::new())), blocked_content: Arc::new(None), - connector_pool: create_http_connector(), + connector_pool: create_http_connector(certificate_path), } } } diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 9323635d17e..698f992fcb5 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -109,13 +109,13 @@ fn create_resource_groups(config_dir: Option<&Path>) cookie_jar: Arc::new(RwLock::new(cookie_jar)), auth_cache: Arc::new(RwLock::new(auth_cache)), hsts_list: Arc::new(RwLock::new(hsts_list.clone())), - connector: create_http_connector(), + connector: create_http_connector("certs"), }; let private_resource_group = ResourceGroup { cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))), auth_cache: Arc::new(RwLock::new(AuthCache::new())), hsts_list: Arc::new(RwLock::new(HstsList::new())), - connector: create_http_connector(), + connector: create_http_connector("certs"), }; (resource_group, private_resource_group) } diff --git a/resources/privatekey_for_testing.key b/resources/privatekey_for_testing.key new file mode 100644 index 00000000000..ffaf2dee876 --- /dev/null +++ b/resources/privatekey_for_testing.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+CuREmlBxE/Ca +amA/y5LJ9RdF4hyJv3/alew/X/x1BiZNdajO1O2VEfIG0iU9terLOg2l8IfuG+Eb +FTOnBIcmGo0vl5OmwEZ1Uhvla+FPqXtOEWEVVnC7/aA+H2GCsp97/2dssMi8//Fl +Mk0UXHvkhjXPO3dwpSiVfIzU2LYXYgua6JFnCG0u629EO61fNF15WoA6seoH1a2t +gTLCsQbapNfUek2T9TCohk2jpkOHxnZNn/KnuM2Anw3N6Ski0Bj+doj/r9xF9CHH +NBng51UMkIGClEJqGj9yzquBd45c09LoG4OAXZKyoQ6q6utidCVYbKh7RaLoHuoq +isg2mUbHAgMBAAECggEBAIIL0/76Flf7DCeu6aReO1nGRSHGRD8i82vyMhOALLMr +/SP+gwDehqH/AL8YKPHcvgpJ9LL8MRiIrXcqAAmnuJAjlT/fGuP+KXj5MivBshIg +aUeX7vZ6C3UpbvFz6fdVInvo365qH0PuZRMZ49MuIn3UNZhVGjvUWTxKWdkBX0IJ +3+aKPEOcx5MInZTr/rNttQq4h898JVM80mzsUBzUzbUgUXJ0vgVyXSBJKGP6PMP2 +pFs/X6QRAvqR69pZ2DarztG7G5EJq2sT2Nymfg4isETiRFM75bPRAmCr+eJQdY7f +jGpxhcTCO0dEP3WgG3M6ZNhtKO4vsm3PhdE06fFdxikCgYEA8GP45n/Yiu272dvX +iKNxYWQ1Yv7A04T7QN8+930/AXIkDw9k86zAstU6Wo47CKseZCKoNMxLO/eD+7tm +SqiMxNEUuxmwb8YYwH/aX27uIdKDahgY6SwLHYFFrBAxU+pm0HVGgLDn6VKPs/db +R31KbJgPr9i2oV1Rt2vha60UIcMCgYEAymH7UEpZ8QRxW1h6lzX/LoTHZHB5t5R5 +UUDR7SErbeM4SpPsJtR2ZuWriW2TAEhBbxgGAGhctLfbdeuYAO+F0PaZlYW/sZx6 +Ei0OWhdd+k/QVj0VHQWCN2oKfjpRj1yYwCcGt7Xei7B0aXVE2A5Aj2bLU+Q9i4x9 +0h3Dac06Uq0CgYA8lLUxQZ7MxETHDoQuxyHXrW1W2WS26Zh4LMqtjD7Imn9D3FlQ +n4SgjOP71kRCVv19ts41IBcFscbtNbj9r6RqJVbYIA063e129cGOs2IH3AmKPzBn +8tWKRf3M8ve7ciMe/a8a13pabpgQfpHeXlDXNSse4bqEyAPD+cgBXsjoCQKBgC1q +jY44ETUADTw1f9U9HdXfoCtO/lGPNSZhyHpRbkCLtA8wYNdZ6HQw6Cy/9TQkAuMe +XgJraRp5A/vTcdoL5li9bjvatujxt4cqq0TWZ5WLobIopPtNSCqNVmt7ROBKJFFC +sMQ7QQTSBV3BHkDp+dz0cX6TAqi1T2r+mOK+Vm9FAoGACIFwOYapBqeDsPwhUiRl +sud+oD28TwbCbQoEVhy5ZoZQZ9S4t7eUaVEYyt/aW1EfeREtvaM5/9DsSmi9f+pw +02XIkZQJplLTDD/0uaCxk0pSJdP9eXkYAEQvEMXs0ING3qIaro2eSVNlO4On9+RY +sz4GyDlleF1ZMsMCTRiNqmg= +-----END PRIVATE KEY----- diff --git a/resources/self_signed_certificate_for_testing.crt b/resources/self_signed_certificate_for_testing.crt new file mode 100644 index 00000000000..db24eb38a9f --- /dev/null +++ b/resources/self_signed_certificate_for_testing.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDlzCCAn+gAwIBAgIJAMVJtbFvDf6vMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJOQzEQMA4GA1UEBwwHUmFsZWlnaDEQMA4GA1UECgwH +TW96aWxsYTEOMAwGA1UECwwFU2Vydm8xEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x +NjEyMjMwNTMyMzFaFw0xNzEyMjMwNTMyMzFaMGIxCzAJBgNVBAYTAlVTMQswCQYD +VQQIDAJOQzEQMA4GA1UEBwwHUmFsZWlnaDEQMA4GA1UECgwHTW96aWxsYTEOMAwG +A1UECwwFU2Vydm8xEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL4K5ESaUHET8JpqYD/Lksn1F0XiHIm/f9qV7D9f/HUG +Jk11qM7U7ZUR8gbSJT216ss6DaXwh+4b4RsVM6cEhyYajS+Xk6bARnVSG+Vr4U+p +e04RYRVWcLv9oD4fYYKyn3v/Z2ywyLz/8WUyTRRce+SGNc87d3ClKJV8jNTYthdi +C5rokWcIbS7rb0Q7rV80XXlagDqx6gfVra2BMsKxBtqk19R6TZP1MKiGTaOmQ4fG +dk2f8qe4zYCfDc3pKSLQGP52iP+v3EX0Icc0GeDnVQyQgYKUQmoaP3LOq4F3jlzT +0ugbg4BdkrKhDqrq62J0JVhsqHtFouge6iqKyDaZRscCAwEAAaNQME4wHQYDVR0O +BBYEFF3/tb9Rfmn4MZ+wepwmZpp/wfkDMB8GA1UdIwQYMBaAFF3/tb9Rfmn4MZ+w +epwmZpp/wfkDMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD+xK7U/ +21bGNLyadlU/4+IZR1ABe0m8QfWNgwIQZGLGOkaiBg8EGzSuyc01uFv6EfnEBXCX +hEs/cc3JA4LDvGQIgkM8yqEJHsFED2X8sNFs9WiTFM2hCeLwcSNAiJYnOwPXKc+t +ObS5CIFZb2yGfgwv0/zTw7mdQNmdk7LiYlOa9EivvuzG/elT76pijWR5ISKUuOeh +JWmGwZb+XimM5DrCfDQ8cdPSMcnb1Jvkf/Rq1UfnBvvuPmI9XJ2MTnLbn6iwugqE +/+lVNcS8FmPZO1R/jhtU44nKhJvT7FgXuisTPrcTi0WdqjVnQAN3ZeUAFZeVfwan +trAwXF0Zvul1HqE= +-----END CERTIFICATE----- diff --git a/tests/unit/net/fetch.rs b/tests/unit/net/fetch.rs index c88c8b1bf59..07bc5aeea4f 100644 --- a/tests/unit/net/fetch.rs +++ b/tests/unit/net/fetch.rs @@ -18,11 +18,17 @@ use hyper::header::{Encoding, Location, Pragma, Quality, QualityItem, SetCookie, use hyper::header::{Headers, Host, HttpDate, Referer as HyperReferer}; use hyper::method::Method; use hyper::mime::{Mime, SubLevel, TopLevel}; -use hyper::server::{Request as HyperRequest, Response as HyperResponse}; +use hyper::net::Openssl; +use hyper::server::{Request as HyperRequest, Response as HyperResponse, Server}; use hyper::status::StatusCode; use hyper::uri::RequestUri; use msg::constellation_msg::TEST_PIPELINE_ID; use net::fetch::cors_cache::CorsCache; +use net::fetch::methods::FetchContext; +use net::filemanager_thread::FileManager; +use net::hsts::HstsEntry; +use net::test::HttpState; +use net_traits::IncludeSubdomains; use net_traits::NetworkError; use net_traits::ReferrerPolicy; use net_traits::request::{Origin, RedirectMode, Referrer, Request, RequestMode}; @@ -506,6 +512,50 @@ fn test_fetch_with_local_urls_only() { assert!(server_response.is_network_error()); } +#[test] +fn test_fetch_with_hsts() { + static MESSAGE: &'static [u8] = b""; + let handler = move |_: HyperRequest, response: HyperResponse| { + response.send(MESSAGE).unwrap(); + }; + + let path = resources_dir_path().expect("Cannot find resource dir"); + let mut cert_path = path.clone(); + cert_path.push("self_signed_certificate_for_testing.crt"); + + let mut key_path = path.clone(); + key_path.push("privatekey_for_testing.key"); + + let ssl = Openssl::with_cert_and_key(cert_path.into_os_string(), key_path.into_os_string()) + .unwrap(); + + let mut server = Server::https("0.0.0.0:0", ssl).unwrap().handle_threads(handler, 1).unwrap(); + + let context = FetchContext { + state: HttpState::new("self_signed_certificate_for_testing.crt"), + user_agent: DEFAULT_USER_AGENT.into(), + devtools_chan: None, + filemanager: FileManager::new(), + }; + + { + let mut list = context.state.hsts_list.write().unwrap(); + list.push(HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None) + .unwrap()); + } + let url_string = format!("http://localhost:{}", server.socket.port()); + let url = ServoUrl::parse(&url_string).unwrap(); + let origin = Origin::Origin(url.origin()); + let mut request = Request::new(url, Some(origin), false, None); + *request.referrer.borrow_mut() = Referrer::NoReferrer; + // Set the flag. + request.local_urls_only = false; + let response = fetch_with_context(request, &context); + let _ = server.close(); + assert_eq!(response.internal_response.unwrap().url().unwrap().scheme(), + "https"); +} + fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response { let handler = move |request: HyperRequest, mut response: HyperResponse| { let redirects = match request.uri { diff --git a/tests/unit/net/lib.rs b/tests/unit/net/lib.rs index 6c2f8c7036f..990247e9ab2 100644 --- a/tests/unit/net/lib.rs +++ b/tests/unit/net/lib.rs @@ -55,7 +55,7 @@ struct FetchResponseCollector { fn new_fetch_context(dc: Option>) -> FetchContext { FetchContext { - state: HttpState::new(), + state: HttpState::new("certs"), user_agent: DEFAULT_USER_AGENT.into(), devtools_chan: dc, filemanager: FileManager::new(),