Skip to content

Commit b25faa4

Browse files
committed
verification: enable ip subj. validation on android.
While the Android verifier defers to the platform verifier for most certificate validation options, it relies on `webpki` for ensuring that a certificate is valid for a given subject name. Since Webpki v0.100.x it's been possible to use `webpki` to validate IP address subject names, and so we want this capability to be enabled for the Android verifier. This commit updates the Android `verifier` to accomplish this. Additionally it enables the mock IP address subject test cases in the mock test suite to ensure things work as expected. In order to support this change the function signature of the inner `Verifier.verify_certificate` fn has to change from accepting a `&str` server name, to also accepting the `&rustls::ServerName` that the trait based `ServerCertVerifier.verify_server_cert` was already accepting. There are two main reasons for this: 1. If we try to pull out a `String` to pass forward from the `rustls::ServerName::IpAddress(&IpAddr)` using `IpAddr.to_string()` we'll get back a "compressed" address for IPv6 addresses. This is problematic when later trying to convert to a `webpki::IpAddrRef` for the validation call using `IpAddrRef::try_from_ascii_str`, because it doesn't support the compressed form. 2. By passing through the `rustls::ServerName` directly we can defer the actual process of interacting with `webpki` to the newly exposed `rustls::client::verify_server_name` fn offered with the `dangerous_configuration` feature. This will ensure the logic for name validation is applied consistently between Rustls and the platform verifier.
1 parent 1a5250c commit b25faa4

File tree

3 files changed

+40
-45
lines changed

3 files changed

+40
-45
lines changed

src/tests/verification_mock/mod.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,11 @@ macro_rules! no_error {
7272
const ROOT1: &[u8] = include_bytes!("root1.crt");
7373
const ROOT1_INT1: &[u8] = include_bytes!("root1-int1.crt");
7474
const ROOT1_INT1_EXAMPLE_COM_GOOD: &[u8] = include_bytes!("root1-int1-ee_example.com-good.crt");
75-
#[cfg(any(windows, target_os = "macos", target_os = "linux"))]
7675
const ROOT1_INT1_LOCALHOST_IPV4_GOOD: &[u8] = include_bytes!("root1-int1-ee_127.0.0.1-good.crt");
77-
#[cfg(any(windows, target_os = "macos", target_os = "linux"))]
7876
const ROOT1_INT1_LOCALHOST_IPV6_GOOD: &[u8] = include_bytes!("root1-int1-ee_1-good.crt");
7977

8078
const EXAMPLE_COM: &str = "example.com";
81-
#[cfg(any(windows, target_os = "macos", target_os = "linux"))]
8279
const LOCALHOST_IPV4: &str = "127.0.0.1";
83-
#[cfg(any(windows, target_os = "macos", target_os = "linux"))]
8480
const LOCALHOST_IPV6: &str = "::1";
8581

8682
#[cfg(any(test, feature = "ffi-testing"))]
@@ -127,35 +123,35 @@ mock_root_test_cases! {
127123
expected_result: Ok(()),
128124
other_error: no_error!(),
129125
},
130-
valid_no_stapling_ipv4 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
126+
valid_no_stapling_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
131127
reference_id: LOCALHOST_IPV4,
132128
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
133129
stapled_ocsp: None,
134130
expected_result: Ok(()),
135131
other_error: no_error!(),
136132
},
137-
valid_no_stapling_ipv6 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
133+
valid_no_stapling_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
138134
reference_id: LOCALHOST_IPV6,
139135
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
140136
stapled_ocsp: None,
141137
expected_result: Ok(()),
142138
other_error: no_error!(),
143139
},
144-
valid_stapled_good_dns [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
140+
valid_stapled_good_dns [ any(windows, target_os = "android", target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
145141
reference_id: EXAMPLE_COM,
146142
chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1],
147143
stapled_ocsp: Some(include_bytes!("root1-int1-ee_example.com-good.ocsp")),
148144
expected_result: Ok(()),
149145
other_error: no_error!(),
150146
},
151-
valid_stapled_good_ipv4 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
147+
valid_stapled_good_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
152148
reference_id: LOCALHOST_IPV4,
153149
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
154150
stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-good.ocsp")),
155151
expected_result: Ok(()),
156152
other_error: no_error!(),
157153
},
158-
valid_stapled_good_ipv6 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
154+
valid_stapled_good_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
159155
reference_id: LOCALHOST_IPV6,
160156
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
161157
stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-good.ocsp")),
@@ -173,14 +169,14 @@ mock_root_test_cases! {
173169
expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)),
174170
other_error: no_error!(),
175171
},
176-
stapled_revoked_ipv4 [ any(windows, target_os = "macos") ] => TestCase {
172+
stapled_revoked_ipv4 [ any(windows, target_os = "android", target_os = "macos") ] => TestCase {
177173
reference_id: LOCALHOST_IPV4,
178174
chain: &[include_bytes!("root1-int1-ee_127.0.0.1-revoked.crt"), ROOT1_INT1],
179175
stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-revoked.ocsp")),
180176
expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)),
181177
other_error: no_error!(),
182178
},
183-
stapled_revoked_ipv6 [ any(windows, target_os = "macos") ] => TestCase {
179+
stapled_revoked_ipv6 [ any(windows, target_os = "android", target_os = "macos") ] => TestCase {
184180
reference_id: LOCALHOST_IPV6,
185181
chain: &[include_bytes!("root1-int1-ee_1-revoked.crt"), ROOT1_INT1],
186182
stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-revoked.ocsp")),
@@ -199,14 +195,14 @@ mock_root_test_cases! {
199195
expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)),
200196
other_error: no_error!(),
201197
},
202-
ee_only_ipv4 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
198+
ee_only_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
203199
reference_id: LOCALHOST_IPV4,
204200
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD],
205201
stapled_ocsp: None,
206202
expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)),
207203
other_error: no_error!(),
208204
},
209-
ee_only_ipv6 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
205+
ee_only_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
210206
reference_id: LOCALHOST_IPV6,
211207
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD],
212208
stapled_ocsp: None,
@@ -221,14 +217,14 @@ mock_root_test_cases! {
221217
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
222218
other_error: no_error!(),
223219
},
224-
domain_mismatch_ipv4 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
220+
domain_mismatch_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
225221
reference_id: "198.168.0.1",
226222
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
227223
stapled_ocsp: None,
228224
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
229225
other_error: no_error!(),
230226
},
231-
domain_mismatch_ipv6 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
227+
domain_mismatch_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
232228
reference_id: "::ffff:c6a8:1",
233229
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
234230
stapled_ocsp: None,
@@ -243,15 +239,15 @@ mock_root_test_cases! {
243239
CertificateError::Other(Arc::from(EkuError)))),
244240
other_error: Some(EkuError),
245241
},
246-
wrong_eku_ipv4 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
242+
wrong_eku_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
247243
reference_id: LOCALHOST_IPV4,
248244
chain: &[include_bytes!("root1-int1-ee_127.0.0.1-wrong_eku.crt"), ROOT1_INT1],
249245
stapled_ocsp: None,
250246
expected_result: Err(TlsError::InvalidCertificate(
251247
CertificateError::Other(Arc::from(EkuError)))),
252248
other_error: Some(EkuError),
253249
},
254-
wrong_eku_ipv6 [ any(windows, target_os = "macos", target_os = "linux") ] => TestCase {
250+
wrong_eku_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
255251
reference_id: LOCALHOST_IPV6,
256252
chain: &[include_bytes!("root1-int1-ee_1-wrong_eku.crt"), ROOT1_INT1],
257253
stapled_ocsp: None,

src/verification/android.rs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ use jni::{
44
JNIEnv,
55
};
66
use rustls::Error::InvalidCertificate;
7-
use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError};
7+
use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError, ServerName};
88
use std::time::SystemTime;
99

1010
use super::{log_server_cert, unsupported_server_name, ALLOWED_EKUS};
1111
use crate::android::{with_context, CachedClass};
12-
use crate::verification::invalid_certificate;
1312

1413
static CERT_VERIFIER_CLASS: CachedClass =
1514
CachedClass::new("org/rustls/platformverifier/CertificateVerifier");
@@ -69,11 +68,12 @@ impl Verifier {
6968
}
7069
}
7170

72-
pub fn verify_certificate(
71+
fn verify_certificate(
7372
&self,
7473
end_entity: &rustls::Certificate,
7574
intermediates: &[rustls::Certificate],
76-
server_name: &str,
75+
server_name: &rustls::ServerName,
76+
server_name_str: &str,
7777
ocsp_response: Option<&[u8]>,
7878
now: SystemTime,
7979
) -> Result<(), TlsError> {
@@ -167,7 +167,7 @@ impl Verifier {
167167
VERIFIER_CALL,
168168
&[
169169
JValue::from(*cx.application_context()),
170-
JValue::from(env.new_string(server_name)?),
170+
JValue::from(env.new_string(server_name_str)?),
171171
JValue::from(env.new_string(AUTH_TYPE)?),
172172
JValue::from(JObject::from(allowed_ekus)),
173173
JValue::from(ocsp_response),
@@ -189,17 +189,10 @@ impl Verifier {
189189
match status {
190190
VerifierStatus::Ok => {
191191
// If everything else was OK, check the hostname.
192-
let cert = webpki::EndEntityCert::try_from(end_entity.as_ref())
193-
.map_err(pki_name_error)?;
194-
let name = webpki::SubjectNameRef::DnsName(
195-
// This unwrap can't fail since it was already validated before.
196-
webpki::DnsNameRef::try_from_ascii_str(server_name).unwrap(),
197-
);
198-
if cert.verify_is_valid_for_subject_name(name).is_ok() {
199-
Ok(())
200-
} else {
201-
Err(InvalidCertificate(CertificateError::NotValidForName))
202-
}
192+
rustls::client::verify_server_name(
193+
&rustls::server::ParsedCertificate::try_from(end_entity)?,
194+
server_name,
195+
)
203196
}
204197
VerifierStatus::Unavailable => Err(TlsError::General(String::from(
205198
"No system trust stores available",
@@ -257,14 +250,6 @@ fn extract_result_info(env: &JNIEnv<'_>, result: JObject<'_>) -> (VerifierStatus
257250
(status, msg.map(String::from))
258251
}
259252

260-
fn pki_name_error(error: webpki::Error) -> TlsError {
261-
use webpki::Error::*;
262-
match error {
263-
BadDer | BadDerTime => InvalidCertificate(CertificateError::BadEncoding),
264-
e => invalid_certificate(format!("invalid peer certificate: {}", e)),
265-
}
266-
}
267-
268253
impl ServerCertVerifier for Verifier {
269254
fn verify_server_cert(
270255
&self,
@@ -280,8 +265,15 @@ impl ServerCertVerifier for Verifier {
280265
) -> Result<rustls::client::ServerCertVerified, TlsError> {
281266
log_server_cert(end_entity);
282267

283-
let server = match server_name {
284-
rustls::ServerName::DnsName(name) => name.as_ref(),
268+
// Verify the server name is one that we support and extract a string to use
269+
// for the platform verifier call.
270+
let ip_name;
271+
let server_name_str = match server_name {
272+
ServerName::DnsName(dns_name) => dns_name.as_ref(),
273+
ServerName::IpAddress(ip_addr) => {
274+
ip_name = ip_addr.to_string();
275+
&ip_name
276+
}
285277
_ => return Err(unsupported_server_name()),
286278
};
287279

@@ -291,7 +283,14 @@ impl ServerCertVerifier for Verifier {
291283
None
292284
};
293285

294-
match self.verify_certificate(end_entity, intermediates, server, ocsp_data, now) {
286+
match self.verify_certificate(
287+
end_entity,
288+
intermediates,
289+
server_name,
290+
server_name_str,
291+
ocsp_data,
292+
now,
293+
) {
295294
Ok(()) => Ok(rustls::client::ServerCertVerified::assertion()),
296295
Err(e) => {
297296
// This error only tells us what the system errored with, so it doesn't leak anything

src/verification/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn unsupported_server_name() -> rustls::Error {
5757

5858
// Unknown certificate error shorthand. Used when we need to construct an "Other" certificate
5959
// error with a platform specific error message.
60-
#[cfg(any(windows, target_os = "android", target_os = "macos", target_os = "ios"))]
60+
#[cfg(any(windows, target_os = "macos", target_os = "ios"))]
6161
fn invalid_certificate(reason: impl Into<String>) -> rustls::Error {
6262
rustls::Error::InvalidCertificate(rustls::CertificateError::Other(std::sync::Arc::from(
6363
Box::from(reason.into()),

0 commit comments

Comments
 (0)