servo: Merge #14716 - Implement HSTS fetch step (from mrnayak:hsts); r=jdm

Implemented step nine of the main fetch. If current URL scheme is 'HTTP' and
current URL's host is domain and if current URL's host matched with Known
HSTS Host Domain Name Matching results in either a superdomain match with
an asserted includeSubDomains directive or a congruent match then we
change request scheme to 'https'. This change has been made in method.rs

A test case to validate this has been added in fetch.rs. For asserting
https scheme, a https localhost was required. For this purpose I have
created a self-signed certificate and refactored fetch-context and
connector.rs to programmatically trust this certificate for running this
test case.

This should fix https://github.com/servo/servo/issues/14363
<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #14363

<!-- Either: -->
- [X] There are tests for these changes

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: c7991d596f7453d09c2b2a98eecce72f182a4e24
This commit is contained in:
Raghav 2016-12-29 00:35:09 -08:00
Родитель 51301d6bd9
Коммит f2c2d3f13a
8 изменённых файлов: 117 добавлений и 9 удалений

Просмотреть файл

@ -27,11 +27,11 @@ const DEFAULT_CIPHERS: &'static str = concat!(
"AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
);
pub fn create_http_connector() -> Arc<Pool<Connector>> {
pub fn create_http_connector(certificate_file: &str) -> Arc<Pool<Connector>> {
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 {

Просмотреть файл

@ -189,7 +189,15 @@ pub fn main_fetch(request: Rc<Request>,
}
// 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

Просмотреть файл

@ -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),
}
}
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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-----

Просмотреть файл

@ -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-----

Просмотреть файл

@ -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 {

Просмотреть файл

@ -55,7 +55,7 @@ struct FetchResponseCollector {
fn new_fetch_context(dc: Option<Sender<DevtoolsControlMsg>>) -> FetchContext {
FetchContext {
state: HttpState::new(),
state: HttpState::new("certs"),
user_agent: DEFAULT_USER_AGENT.into(),
devtools_chan: dc,
filemanager: FileManager::new(),