Moves the HSTS replacement code to http_loader

This respects STS for redirects as well.
This commit is contained in:
Sam Gibson 2015-07-18 17:23:46 +10:00
parent 826f56bdf3
commit f2148f06b1
4 changed files with 49 additions and 50 deletions

View file

@ -9,7 +9,6 @@ use url::Url;
use std::str::{from_utf8}; use std::str::{from_utf8};
use net_traits::LoadData;
use util::resource_files::read_resource_file; use util::resource_files::read_resource_file;
static IPV4_REGEX: Regex = regex!( static IPV4_REGEX: Regex = regex!(
@ -64,7 +63,7 @@ impl HSTSEntry {
} }
} }
#[derive(RustcDecodable, RustcEncodable)] #[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct HSTSList { pub struct HSTSList {
pub entries: Vec<HSTSEntry> pub entries: Vec<HSTSEntry>
} }
@ -126,20 +125,13 @@ pub fn preload_hsts_domains() -> Option<HSTSList> {
}) })
} }
pub fn secure_load_data(load_data: &LoadData) -> LoadData { pub fn secure_url(url: &Url) -> Url {
if &*load_data.url.scheme == "http" { if &*url.scheme == "http" {
let mut secure_load_data = load_data.clone(); let mut secure_url = url.clone();
let mut secure_url = load_data.url.clone();
secure_url.scheme = "https".to_string(); secure_url.scheme = "https".to_string();
// The Url struct parses the port for a known scheme only once. Url::parse(&secure_url.serialize()).unwrap()
// Updating the scheme doesn't update the port internally, resulting in
// HTTPS connections attempted on port 80. Serialising and re-parsing
// the Url is a hack to get around this.
secure_load_data.url = Url::parse(&secure_url.serialize()).unwrap();
secure_load_data
} else { } else {
load_data.clone() url.clone()
} }
} }

View file

@ -7,6 +7,7 @@ use net_traits::ProgressMsg::{Payload, Done};
use devtools_traits::{DevtoolsControlMsg, NetworkEvent}; use devtools_traits::{DevtoolsControlMsg, NetworkEvent};
use mime_classifier::MIMEClassifier; use mime_classifier::MIMEClassifier;
use resource_task::{start_sending_opt, start_sending_sniffed_opt}; use resource_task::{start_sending_opt, start_sending_sniffed_opt};
use hsts::{HSTSList, secure_url};
use log; use log;
use std::collections::HashSet; use std::collections::HashSet;
@ -33,11 +34,13 @@ use uuid;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::boxed::FnBox; use std::boxed::FnBox;
pub fn factory(cookies_chan: Sender<ControlMsg>, devtools_chan: Option<Sender<DevtoolsControlMsg>>) pub fn factory(cookies_chan: Sender<ControlMsg>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
hsts_list: Option<HSTSList>)
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> { -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
box move |load_data, senders, classifier| { box move |load_data, senders, classifier| {
spawn_named("http_loader".to_owned(), spawn_named("http_loader".to_owned(),
move || load(load_data, senders, classifier, cookies_chan, devtools_chan)) move || load(load_data, senders, classifier, cookies_chan, devtools_chan, hsts_list))
} }
} }
@ -69,8 +72,21 @@ fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
} }
} }
fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>, fn request_must_be_secured(hsts_list: Option<&HSTSList>, url: &Url) -> bool {
cookies_chan: Sender<ControlMsg>, devtools_chan: Option<Sender<DevtoolsControlMsg>>) { match (hsts_list.as_ref(), url.domain()) {
(Some(ref l), Some(ref h)) => {
l.is_host_secure(h)
},
_ => false
}
}
fn load(mut load_data: LoadData,
start_chan: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cookies_chan: Sender<ControlMsg>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
hsts_list: Option<HSTSList>) {
// FIXME: At the time of writing this FIXME, servo didn't have any central // FIXME: At the time of writing this FIXME, servo didn't have any central
// location for configuration. If you're reading this and such a // location for configuration. If you're reading this and such a
// repository DOES exist, please update this constant to use it. // repository DOES exist, please update this constant to use it.
@ -101,6 +117,11 @@ fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEC
loop { loop {
iters = iters + 1; iters = iters + 1;
if request_must_be_secured(hsts_list.as_ref(), &url) {
info!("{} is in the strict transport security list, requesting secure host", url);
url = secure_url(&url);
}
if iters > max_redirects { if iters > max_redirects {
send_error(url, "too many redirects".to_string(), start_chan); send_error(url, "too many redirects".to_string(), start_chan);
return; return;

View file

@ -23,7 +23,6 @@ use hsts::HSTSList;
use hsts::HSTSEntry; use hsts::HSTSEntry;
use hsts::Subdomains; use hsts::Subdomains;
use hsts::preload_hsts_domains; use hsts::preload_hsts_domains;
use hsts::secure_load_data;
use devtools_traits::{DevtoolsControlMsg}; use devtools_traits::{DevtoolsControlMsg};
use hyper::header::{ContentType, Header, SetCookie, UserAgent}; use hyper::header::{ContentType, Header, SetCookie, UserAgent};
@ -293,25 +292,16 @@ impl ResourceManager {
} }
} }
pub fn is_host_sts(&self, host: &str) -> bool {
match self.hsts_list.as_ref() {
Some(list) => list.is_host_secure(host),
None => false
}
}
pub fn add_hsts_entry(&mut self, entry: HSTSEntry) { pub fn add_hsts_entry(&mut self, entry: HSTSEntry) {
if let Some(list) = self.hsts_list.as_mut() { if let Some(list) = self.hsts_list.as_mut() {
list.push(entry) list.push(entry)
} }
} }
fn load_data_must_be_secured(&self, load_data: &LoadData) -> bool { pub fn is_host_sts(&self, host: &str) -> bool {
match (self.hsts_list.as_ref(), load_data.url.domain()) { match self.hsts_list.as_ref() {
(Some(ref l), Some(ref h)) => { Some(list) => list.is_host_secure(host),
l.is_host_secure(h) None => false
},
_ => false
} }
} }
@ -326,10 +316,6 @@ impl ResourceManager {
load_data.preserved_headers.set(UserAgent(ua.clone())); load_data.preserved_headers.set(UserAgent(ua.clone()));
}); });
if self.load_data_must_be_secured(&load_data) {
load_data = secure_load_data(&load_data)
}
fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>)) fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>))
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> { -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
box move |load_data, senders, classifier| { box move |load_data, senders, classifier| {
@ -340,7 +326,7 @@ impl ResourceManager {
let loader = match &*load_data.url.scheme { let loader = match &*load_data.url.scheme {
"file" => from_factory(file_loader::factory), "file" => from_factory(file_loader::factory),
"http" | "https" | "view-source" => "http" | "https" | "view-source" =>
http_loader::factory(self.resource_task.clone(), self.devtools_chan.clone()), http_loader::factory(self.resource_task.clone(), self.devtools_chan.clone(), self.hsts_list.clone()),
"data" => from_factory(data_loader::factory), "data" => from_factory(data_loader::factory),
"about" => from_factory(about_loader::factory), "about" => from_factory(about_loader::factory),
_ => { _ => {

View file

@ -5,7 +5,7 @@
use net::hsts::HSTSList; use net::hsts::HSTSList;
use net::hsts::HSTSEntry; use net::hsts::HSTSEntry;
use net::hsts::Subdomains; use net::hsts::Subdomains;
use net::hsts::secure_load_data; use net::hsts::secure_url;
use net::resource_task::ResourceManager; use net::resource_task::ResourceManager;
use net_traits::LoadData; use net_traits::LoadData;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
@ -248,26 +248,26 @@ fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
} }
#[test] #[test]
fn test_secure_load_data_does_not_change_explicit_port() { fn test_secure_url_does_not_change_explicit_port() {
let load_data = LoadData::new(Url::parse("http://mozilla.org:8080/").unwrap(), None); let url = Url::parse("http://mozilla.org:8080/").unwrap();
let secure = secure_load_data(&load_data); let secure = secure_url(&url);
assert!(secure.url.port().unwrap() == 8080u16); assert!(secure.port().unwrap() == 8080u16);
} }
#[test] #[test]
fn test_secure_load_data_does_not_affect_non_http_schemas() { fn test_secure_url_does_not_affect_non_http_schemas() {
let load_data = LoadData::new(Url::parse("file://mozilla.org").unwrap(), None); let url = Url::parse("file://mozilla.org").unwrap();
let secure = secure_load_data(&load_data); let secure = secure_url(&url);
assert_eq!(&secure.url.scheme, "file"); assert_eq!(&secure.scheme, "file");
} }
#[test] #[test]
fn test_secure_load_data_forces_an_http_host_in_list_to_https() { fn test_secure_url_forces_an_http_host_in_list_to_https() {
let load_data = LoadData::new(Url::parse("http://mozilla.org").unwrap(), None); let url = Url::parse("http://mozilla.org").unwrap();
let secure = secure_load_data(&load_data); let secure = secure_url(&url);
assert_eq!(&secure.url.scheme, "https"); assert_eq!(&secure.scheme, "https");
} }