Skip to content

Commit 7ada714

Browse files
committed
Prefer partial success to failure
If we read some certs from a file, but fail to read some from a directory (or vv.) then return what we have. This is good because it restores the crate's previous behaviour. It also matches what (for example) golang crypto.x509 does. This is bad because it hides a failure, which would be confusing if a user relied on reading from _both_ a file and directory at the same time. Would someone do that? The follow commit steps towards ameliorating this, slightly.
1 parent 9832a72 commit 7ada714

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

src/lib.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,33 @@ impl CertPaths {
157157
return Ok(None);
158158
}
159159

160+
let mut first_error = None;
161+
160162
let mut certs = match &self.file {
161-
Some(cert_file) => {
162-
load_pem_certs(cert_file).map_err(|err| Self::load_err(cert_file, "file", err))?
163-
}
163+
Some(cert_file) => match load_pem_certs(cert_file)
164+
.map_err(|err| Self::load_err(cert_file, "file", err))
165+
{
166+
Ok(certs) => certs,
167+
Err(err) => {
168+
first_error = first_error.or(Some(err));
169+
Vec::new()
170+
}
171+
},
164172
None => Vec::new(),
165173
};
166174

167175
if let Some(cert_dir) = &self.dir {
168-
certs.append(
169-
&mut load_pem_certs_from_dir(cert_dir)
170-
.map_err(|err| Self::load_err(cert_dir, "dir", err))?,
171-
);
176+
match load_pem_certs_from_dir(cert_dir)
177+
.map_err(|err| Self::load_err(cert_dir, "dir", err))
178+
{
179+
Ok(mut from_dir) => certs.append(&mut from_dir),
180+
Err(err) => first_error = first_error.or(Some(err)),
181+
}
182+
}
183+
184+
// promote first error if we have no certs to return
185+
if let (Some(error), []) = (first_error, certs.as_slice()) {
186+
return Err(error);
172187
}
173188

174189
certs.sort_unstable_by(|a, b| a.cmp(b));

tests/smoketests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,53 @@ fn badssl_with_dir_from_env() {
184184

185185
check_site("self-signed.badssl.com").unwrap();
186186
}
187+
188+
#[test]
189+
#[serial]
190+
#[ignore]
191+
#[cfg(target_os = "linux")]
192+
fn google_with_dir_but_broken_file() {
193+
unsafe {
194+
// SAFETY: safe because of #[serial]
195+
common::clear_env();
196+
}
197+
198+
env::set_var("SSL_CERT_DIR", "/etc/ssl/certs");
199+
env::set_var("SSL_CERT_FILE", "not-exist");
200+
check_site("google.com").unwrap();
201+
}
202+
203+
#[test]
204+
#[serial]
205+
#[ignore]
206+
#[cfg(target_os = "linux")]
207+
fn google_with_file_but_broken_dir() {
208+
unsafe {
209+
// SAFETY: safe because of #[serial]
210+
common::clear_env();
211+
}
212+
213+
env::set_var("SSL_CERT_DIR", "/not-exist");
214+
env::set_var("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt");
215+
check_site("google.com").unwrap();
216+
}
217+
218+
#[test]
219+
#[serial]
220+
#[ignore]
221+
#[cfg(target_os = "linux")]
222+
fn nothing_works_with_broken_file_and_dir() {
223+
unsafe {
224+
// SAFETY: safe because of #[serial]
225+
common::clear_env();
226+
}
227+
228+
env::set_var("SSL_CERT_DIR", "/not-exist");
229+
env::set_var("SSL_CERT_FILE", "not-exist");
230+
assert_eq!(
231+
rustls_native_certs::load_native_certs()
232+
.unwrap_err()
233+
.to_string(),
234+
"could not load certs from file not-exist: No such file or directory (os error 2)"
235+
);
236+
}

0 commit comments

Comments
 (0)