servo: Merge #6722 - Obey Strict-Transport-Security header (from samfoo:sts-headers); r=jdm

Resolves #6703.

Done:

* [x] When STS headers received, add the host to the HSTS list

Todo:

* [ ] Persist the in-memory list so that it's reloaded on the next browser boot
* [ ] Add tests to `http_loader::load` - it's pretty well completely untested right now, but it's a bit gnarly to untangle and without mocking, it's hard to deal with the dependency on making a real network request. Writing a mock request object should be doable for testing, but there's a lot going on in the function right now.

Source-Repo: https://github.com/servo/servo
Source-Revision: f1c26c59f177570a697c420308fb89d9773c7468
This commit is contained in:
Sam Gibson 2015-08-02 11:14:02 -06:00
parent e69b59f7ca
commit 34d2f0c3a8
3 changed files with 35 additions and 12 deletions

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer};
use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer, IncludeSubdomains};
use net_traits::ProgressMsg::{Payload, Done};
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, NetworkEvent};
use mime_classifier::MIMEClassifier;
@ -15,8 +15,8 @@ use std::collections::HashSet;
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hyper::client::Request;
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem};
use hyper::header::{Quality, QualityItem};
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem};
use hyper::header::StrictTransportSecurity;
use hyper::Error as HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
@ -37,13 +37,13 @@ use uuid;
use std::borrow::ToOwned;
use std::boxed::FnBox;
pub fn factory(cookies_chan: IpcSender<ControlMsg>,
pub fn factory(resource_mgr_chan: IpcSender<ControlMsg>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
hsts_list: Arc<Mutex<HSTSList>>)
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
box move |load_data, senders, classifier| {
spawn_named("http_loader".to_owned(),
move || load(load_data, senders, classifier, cookies_chan, devtools_chan, hsts_list))
move || load(load_data, senders, classifier, resource_mgr_chan, devtools_chan, hsts_list))
}
}
@ -87,7 +87,7 @@ fn request_must_be_secured(hsts_list: &HSTSList, url: &Url) -> bool {
fn load(mut load_data: LoadData,
start_chan: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cookies_chan: IpcSender<ControlMsg>,
resource_mgr_chan: IpcSender<ControlMsg>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
hsts_list: Arc<Mutex<HSTSList>>) {
// FIXME: At the time of writing this FIXME, servo didn't have any central
@ -204,7 +204,7 @@ reason: \"certificate verify failed\" }]))";
}
let (tx, rx) = ipc::channel().unwrap();
cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(),
resource_mgr_chan.send(ControlMsg::GetCookiesForUrl(url.clone(),
tx,
CookieSource::HTTP)).unwrap();
if let Some(cookie_list) = rx.recv().unwrap() {
@ -291,13 +291,36 @@ reason: \"certificate verify failed\" }]))";
if let Some(cookies) = response.headers.get_raw("set-cookie") {
for cookie in cookies.iter() {
if let Ok(cookies) = String::from_utf8(cookie.clone()) {
cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
cookies,
CookieSource::HTTP)).unwrap();
resource_mgr_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
cookies,
CookieSource::HTTP)).unwrap();
}
}
}
if url.scheme == "https" {
if let Some(header) = response.headers.get::<StrictTransportSecurity>() {
if let Some(host) = url.domain() {
info!("adding host {} to the strict transport security list", host);
info!("- max-age {}", header.max_age);
let include_subdomains = if header.include_subdomains {
info!("- includeSubdomains");
IncludeSubdomains::Included
} else {
IncludeSubdomains::NotIncluded
};
resource_mgr_chan.send(
ControlMsg::SetHSTSEntryForHost(
host.to_string(), include_subdomains, header.max_age
)
).unwrap();
}
}
}
if response.status.class() == StatusClass::Redirection {
match response.headers.get::<Location>() {
Some(&Location(ref new_url)) => {

View File

@ -238,7 +238,7 @@ impl ResourceChannelManager {
consumer.send(self.resource_manager.cookie_storage.cookies_for_url(&url, source)).unwrap();
}
ControlMsg::SetHSTSEntryForHost(host, include_subdomains, max_age) => {
if let Some(entry) = HSTSEntry::new(host, include_subdomains, max_age) {
if let Some(entry) = HSTSEntry::new(host, include_subdomains, Some(max_age)) {
self.resource_manager.add_hsts_entry(entry)
}
}

View File

@ -145,7 +145,7 @@ pub enum ControlMsg {
/// Retrieve the stored cookies for a given URL
GetCookiesForUrl(Url, IpcSender<Option<String>>, CookieSource),
/// Store a domain's STS information
SetHSTSEntryForHost(String, IncludeSubdomains, Option<u64>),
SetHSTSEntryForHost(String, IncludeSubdomains, u64),
Exit
}