Make SSL cert verification errors work again. Add a horrible, no-good, very bad regression test.

This commit is contained in:
Josh Matthews 2015-08-03 17:01:00 -04:00
parent 50be4bb09e
commit 5e123df7a7
4 changed files with 63 additions and 20 deletions

View file

@ -27,6 +27,7 @@ use msg::constellation_msg::{PipelineId};
use net_traits::ProgressMsg::{Done, Payload};
use net_traits::hosts::replace_hosts;
use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadData, Metadata};
use openssl::ssl::error::{SslError, OpensslError};
use openssl::ssl::{SSL_VERIFY_PEER, SslContext, SslMethod};
use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
@ -210,29 +211,21 @@ impl HttpRequestFactory for NetworkHttpRequestFactory {
fn create(&self, url: Url, method: Method) -> Result<WrappedHttpRequest, LoadError> {
let connection = Request::with_connector(method, url.clone(), &*self.connector);
let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]))";
if let Err(HttpError::Ssl(ref error)) = connection {
let error: &(Error + Send + 'static) = &**error;
if let Some(&SslError::OpenSslErrors(ref errors)) = error.downcast_ref::<SslError>() {
if errors.iter().any(is_cert_verify_error) {
return Err(
LoadError::Ssl(url, format!("ssl error: {:?} {:?}",
error.description(),
error.cause())));
}
}
}
let request = match connection {
Ok(req) => req,
Err(HttpError::Io(ref io_error)) if (
io_error.kind() == io::ErrorKind::Other &&
io_error.description() == "Error in OpenSSL" &&
// FIXME: This incredibly hacky. Make it more robust, and at least test it.
format!("{:?}", io_error.cause()) == ssl_err_string
) => {
return Err(
LoadError::Ssl(
url,
format!("ssl error {:?}: {:?} {:?}",
io_error.kind(),
io_error.description(),
io_error.cause())
)
)
},
Err(e) => {
return Err(LoadError::Connection(url, e.description().to_owned()))
}
@ -756,3 +749,14 @@ fn send_data<R: Read>(reader: &mut R,
let _ = progress_chan.send(Done(Ok(())));
}
// FIXME: This incredibly hacky. Make it more robust, and at least test it.
fn is_cert_verify_error(error: &OpensslError) -> bool {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
library == "SSL routines" &&
function == "SSL3_GET_SERVER_CERTIFICATE" &&
reason == "certificate verify failed"
}
}
}

View file

@ -4559,6 +4559,12 @@
"url": "/_mozilla/mozilla/MouseEvent.html"
}
],
"mozilla/bad_cert_detected.html": [
{
"path": "mozilla/bad_cert_detected.html",
"url": "/_mozilla/mozilla/bad_cert_detected.html"
}
],
"mozilla/blob.html": [
{
"path": "mozilla/blob.html",
@ -9622,4 +9628,4 @@
"rev": null,
"url_base": "/_mozilla/",
"version": 2
}
}

View file

@ -0,0 +1,28 @@
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/origin_helpers.js?pipe=sub"></script>
</head>
<body>
<script>
var t = async_test("Invalid SSL cert noticed");
t.step(function() {
var target = location.href.replace(HTTP_ORIGIN, HTTPS_ORIGIN)
.replace('bad_cert_detected.html',
'resources/origin_helpers.js');
// Servo currently lacks the ability to introspect any content that is blocked
// due to a cert error, so we use a roundabout method to infer that that's happened.
// When the worker has a cert failure, that translates into attempting to evaluate the
// contents of badcert.html as JS, which triggers an exception that currently does not
// propagate to the parent scope. If we _do_ get an error event in the parent scope,
// that means that the cert verification was treated no different than any other
// network error, since we dispatch an error event in that case.
var w = new Worker(target);
w.addEventListener('error', t.unreached_func("cert not detected as invalid"), false);
// We infer that we detected an invalid cert if nothing happens for a few seconds.
setTimeout(function() { t.done() }, 3000);
});
</script>
</body>
</html>

View file

@ -0,0 +1,5 @@
var HTTP_PORT = '{{ports[http][0]}}';
var HTTPS_PORT = '{{ports[https][0]}}';
var ORIGINAL_HOST = '\'{{host}}\'';
var HTTP_ORIGIN = 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT;
var HTTPS_ORIGIN = 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT;