Print out useful SSL-related information on SSL error page.

This commit is contained in:
Josh Matthews 2016-06-29 12:29:03 -04:00
parent 810735a846
commit 94df389031
5 changed files with 45 additions and 11 deletions

View file

@ -33,6 +33,7 @@ use net_traits::hosts::replace_hosts;
use net_traits::response::HttpsState; use net_traits::response::HttpsState;
use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData}; use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData};
use net_traits::{Metadata, NetworkError, RequestSource, CustomResponse}; use net_traits::{Metadata, NetworkError, RequestSource, CustomResponse};
use openssl;
use openssl::ssl::error::{SslError, OpensslError}; use openssl::ssl::error::{SslError, OpensslError};
use profile_traits::time::{ProfilerCategory, profile, ProfilerChan, TimerMetadata}; use profile_traits::time::{ProfilerCategory, profile, ProfilerChan, TimerMetadata};
use profile_traits::time::{TimerMetadataReflowType, TimerMetadataFrameType}; use profile_traits::time::{TimerMetadataReflowType, TimerMetadataFrameType};
@ -143,8 +144,8 @@ fn load_for_consumer(load_data: LoadData,
Err(error) => { Err(error) => {
match error.error { match error.error {
LoadErrorType::ConnectionAborted { .. } => unreachable!(), LoadErrorType::ConnectionAborted { .. } => unreachable!(),
LoadErrorType::Ssl { .. } => send_error(error.url.clone(), LoadErrorType::Ssl { reason } => send_error(error.url.clone(),
NetworkError::SslValidation(error.url), NetworkError::SslValidation(error.url, reason),
start_chan), start_chan),
LoadErrorType::Cancelled => send_error(error.url, NetworkError::LoadCancelled, start_chan), LoadErrorType::Cancelled => send_error(error.url, NetworkError::LoadCancelled, start_chan),
_ => send_error(error.url, NetworkError::Internal(error.error.description().to_owned()), start_chan) _ => send_error(error.url, NetworkError::Internal(error.error.description().to_owned()), start_chan)
@ -259,8 +260,21 @@ impl HttpRequestFactory for NetworkHttpRequestFactory {
let error: &(Error + Send + 'static) = &**error; let error: &(Error + Send + 'static) = &**error;
if let Some(&SslError::OpenSslErrors(ref errors)) = error.downcast_ref::<SslError>() { if let Some(&SslError::OpenSslErrors(ref errors)) = error.downcast_ref::<SslError>() {
if errors.iter().any(is_cert_verify_error) { if errors.iter().any(is_cert_verify_error) {
let msg = format!("ssl error: {:?} {:?}", error.description(), error.cause()); let mut error_report = vec![format!("ssl error ({}):", openssl::version::version())];
return Err(LoadError::new(url, LoadErrorType::Ssl { reason: msg })); let mut suggestion = None;
for err in errors {
if is_unknown_message_digest_err(err) {
suggestion = Some("<b>Servo recommends upgrading to a newer OpenSSL version.</b>");
}
error_report.push(format_ssl_error(err));
}
if let Some(suggestion) = suggestion {
error_report.push(suggestion.to_owned());
}
let error_report = error_report.join("<br>\n");
return Err(LoadError::new(url, LoadErrorType::Ssl { reason: error_report }));
} }
} }
} }
@ -1126,6 +1140,24 @@ fn is_cert_verify_error(error: &OpensslError) -> bool {
} }
} }
fn is_unknown_message_digest_err(error: &OpensslError) -> bool {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
library == "asn1 encoding routines" &&
function == "ASN1_item_verify" &&
reason == "unknown message digest algorithm"
}
}
}
fn format_ssl_error(error: &OpensslError) -> String {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
format!("{}: {} - {}", library, function, reason)
}
}
}
fn to_resource_type(context: &LoadContext) -> ResourceType { fn to_resource_type(context: &LoadContext) -> ResourceType {
match *context { match *context {
LoadContext::Browsing => ResourceType::Document, LoadContext::Browsing => ResourceType::Document,

View file

@ -158,8 +158,8 @@ fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata,
} }
LoadConsumer::Listener(target) => { LoadConsumer::Listener(target) => {
match network_error { match network_error {
Some(NetworkError::SslValidation(url)) => { Some(NetworkError::SslValidation(url, reason)) => {
let error = NetworkError::SslValidation(url); let error = NetworkError::SslValidation(url, reason);
target.invoke_with_listener(ResponseAction::HeadersAvailable(Err(error))); target.invoke_with_listener(ResponseAction::HeadersAvailable(Err(error)));
} }
_ => target.invoke_with_listener(ResponseAction::HeadersAvailable(Ok(metadata))), _ => target.invoke_with_listener(ResponseAction::HeadersAvailable(Ok(metadata))),

View file

@ -667,7 +667,7 @@ pub enum NetworkError {
Internal(String), Internal(String),
LoadCancelled, LoadCancelled,
/// SSL validation error that has to be handled in the HTML parser /// SSL validation error that has to be handled in the HTML parser
SslValidation(Url), SslValidation(Url, String),
} }
/// Normalize `slice`, as defined by /// Normalize `slice`, as defined by

View file

@ -81,11 +81,11 @@ impl ParserContext {
impl AsyncResponseListener for ParserContext { impl AsyncResponseListener for ParserContext {
fn headers_available(&mut self, meta_result: Result<Metadata, NetworkError>) { fn headers_available(&mut self, meta_result: Result<Metadata, NetworkError>) {
let mut is_ssl_error = false; let mut ssl_error = None;
let metadata = match meta_result { let metadata = match meta_result {
Ok(meta) => Some(meta), Ok(meta) => Some(meta),
Err(NetworkError::SslValidation(url)) => { Err(NetworkError::SslValidation(url, reason)) => {
is_ssl_error = true; ssl_error = Some(reason);
let mut meta = Metadata::default(url); let mut meta = Metadata::default(url);
let mime: Option<Mime> = "text/html".parse().ok(); let mime: Option<Mime> = "text/html".parse().ok();
meta.set_content_type(mime.as_ref()); meta.set_content_type(mime.as_ref());
@ -124,10 +124,11 @@ impl AsyncResponseListener for ParserContext {
parser.set_plaintext_state(); parser.set_plaintext_state();
}, },
Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
if is_ssl_error { if let Some(reason) = ssl_error {
self.is_synthesized_document = true; self.is_synthesized_document = true;
let page_bytes = read_resource_file("badcert.html").unwrap(); let page_bytes = read_resource_file("badcert.html").unwrap();
let page = String::from_utf8(page_bytes).unwrap(); let page = String::from_utf8(page_bytes).unwrap();
let page = page.replace("${reason}", &reason);
parser.pending_input().borrow_mut().push(page); parser.pending_input().borrow_mut().push(page);
parser.parse_sync(); parser.parse_sync();
} }

View file

@ -4,5 +4,6 @@
</head> </head>
<body> <body>
<img src="chrome://resources/badcert.jpg"> <img src="chrome://resources/badcert.jpg">
<p>${reason}</p>
</body> </body>
</html> </html>