rustls-native-certs
rustls-native-certs copied to clipboard
FreeBSD certs not found
I have a FreeBSD 13 system and I have uploaded my custom certs into /usr/local/share/certs/ca-root-nss.crt which allows OOTB curl to work. However, Rust programs (ex: rustup-init) built using rustls-native-certs do not check that location. I can work around the issue by setting the env variable SSL_CERT_FILE=/usr/local/share/certs/ca-root-nss.crt but I would like for that location to be searched by this crate.
As an update to this issue, it looks like the file /usr/local/share/certs/ca-root-nss.crt isn't searched because /usr/local/openssl/cert.pem also exists and is higher in the priority search order.
Should all possible locations be searched and aggregated?
I would think all possible locations should be searched and aggregated but not sure if that would be a breaking change?
On Tue, Sep 14, 2021 at 5:53 PM Benjamin Saunders @.***> wrote:
Should all possible locations be searched and aggregated?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/rustls/rustls-native-certs/issues/28#issuecomment-919567070, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAK7YKAJ2GTHTDSTAXJZVQDUB7HABANCNFSM5EBATEIQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Would you mind submitting a PR for aggregating? It seems reasonable to me.
I'm interested in this (as I believe it's ultimately something that is affecting one of the users of my project), and willing to submit a PR, however it looks like the precedence searching is actually internal to openssl-probe
and they don't currently expose an API of the files that are searched—They provide an API for the directories, but not the files within those directories that are searched as part of probe
.
I'm a bit out of my depth with the internals here, but it looks like openssl-probe
is intentionally only providing a single file and single directory as the results of probe
—Does anyone know if it even makes sense to search all of the possible locations? Or would that be behavior that is wildly different from what OpenSSL actually does to detect certificates?
I just ran into the ~~same~~ simalar issue on FreeBSD 14. Looking at openssl-probe, I believe the /etc/ssh/certs/ is what openssl-probe is supposed to find. Openssl-probe can return a file and a directory:
pub struct ProbeResult {
pub cert_file: [Option](https://doc.rust-lang.org/nightly/core/option/enum.Option.html)<[PathBuf](https://doc.rust-lang.org/nightly/std/path/struct.PathBuf.html)>,
pub cert_dir: [Option](https://doc.rust-lang.org/nightly/core/option/enum.Option.html)<[PathBuf](https://doc.rust-lang.org/nightly/std/path/struct.PathBuf.html)>,
}
But this crate only checks for ProbeResult::cert_file:
pub fn load_native_certs() -> Result<Vec<CertificateDer<'static>>, Error> {
let likely_locations = openssl_probe::probe();
match likely_locations.cert_file {
Some(cert_file) => load_pem_certs(&cert_file),
None => Ok(Vec::new()),
}
}
I suspect other tools that use probe() use both and that's why this works elsewhere. Still it seems odd to stop on the first file and directory.
Okay, reading certs from ProbeResult::cert_dir
seems indeed to fix the issue on FreeBSD 14. See my draft commit. Tests now pass on FreeBSD 14.
I'm not sure this solves the issue originally described here but it appears fix the issue I had where no cert would be found at all on the system because there is only a /etc/ssl/certs/ here.
In rustup, this seemed to get fixed by running pkg install -y ca_root_nss
. How does that interact with this?
It generally seems sensible to me to work through both cert_file
and cert_dir
. @ctz any thoughts?
Yeah we probably should. That would involve reading in files named in the openssl rehash
or certctl rehash
style (hopefully the same -- needs a little research).
Working through both cert_file
and cert_dir
is likely to end up with every root twice. We could look at cert_dir
only if cert_file
does not exist, or perhaps deduplicate as we go?
@pgerber want to submit a PR for this?
Can I assume the goal here is that rustls-native-certs is designed to be a OpenSSL drop-in replacement? In that case, we should probably try to be closer to what OpenSSL does, or at least what the openssl Crate does.
Reading through (SSL_CTX_load_verify_locations), there is some differences:
(Env. vars. are documented in openssl-env.)
Certs are looked up based on hash and only when needed:
If CApath is not NULL, it points to a directory containing CA certificates in PEM format. The files each contain one CA certificate. The files are looked up by the CA subject name hash value, which must hence be available. If more than one CA certificate with the same name hash value exist, the extension must be different (e.g. 9d66eef0.0, 9d66eef0.1 etc). The search is performed in the ordering of the extension number, regardless of other properties of the certificates. Use the c_rehash utility to create the necessary links.
The certificates in CApath are only looked up when required, e.g. when building the certificate chain or when actually performing the verification of a peer certificate.
See also See also c_rehash.
CAfile is searched first:
When looking up CA certificates, the OpenSSL library will first search the certificates in CAfile, then those in CApath.
I also looked at openssl-probe a bit closer:
SSL_CERT_FILE/SSL_CERT_DIR are already consulted when calling probe():
fn probe_from_env() -> ProbeResult {
let var = |name| {
env::var_os(name)
.map(PathBuf::from)
.filter(|p| p.exists())
};
ProbeResult {
cert_file: var(ENV_CERT_FILE),
cert_dir: var(ENV_CERT_DIR),
}
}
pub fn probe() -> ProbeResult {
let mut result = probe_from_env();
for certs_dir in cert_dirs_iter() {
…
}
This implementation differs from the implementation (in my draft) in two ways: a) SSL_CERT_FILE/SSL_CERT_DIR is ignored if the target doesn't exist. b) If only one of SSL_CERT_FILE and SSL_CERT_DIR is set and the path exists, the other one is still looked up at the default location.
If we want to be compatible with OpenSSL, we should probably use this implementation.
According Compilation and Installation:
One is supposed to select default directory / file at build time:
OpenSSL 1.1.0 and above
OpenSSL 1.1.0 changed the behavior of install rules. You should specify both --prefix and --openssldir to ensure make install works as expected.
The takeaway is /usr/local/ssl is used by default, and it can be overridden with both --prefix and --openssldir. The rule of thumb applies for path overrides: specify both --prefix and --openssldir. So, it would appear that there is usually no more than one file and one directory. So, this would match
openssl-probe
.
So there is but one directory and one file searched and their paths are platform/OS/distribution-dependent. So, probe()
only returning one location for the file and one for the directory appears to match the behavior of OpenSSL.
Can I assume the goal here is that rustls-native-certs is designed to be a OpenSSL drop-in replacement?
No, merely enough to obtain certificates for a variety of operating systems. That is why the current implementation is good enough for linux (because update-ca-certificates
is pretty ubiquitous, and maintains a the single-file version, albeit nobody can agree where a file lives).
Your attached commit looks like an excellent start to me.
Okay, having given this some though, I'll open a pull request with the following changes (as compared to my draft commit):
- Remove parsing for SSL_CERT_FILE and have
openssl_probe::probe()
handling SSL_CERT_FILE and SSL_CERT_DIR. This way, openssl and rustls have better compatibility. (Main difference is that openssl-probe does not err if the file doesn't exist.) - Parse files that have a file name which appears to be a valid hash in CAdir.
- Deduplicate certificates.
For the openssl-probe Crate:
- Create a pull request documenting
probe()
, particularly, document how it handles SSL_CERT_FILE/SSL_CERT_DIR. - Create a pull request to handle an empty SSL_CERT_FILE/SSL_CERT_DIR differently. Currently, this is interpreted as path "." but OpenSSL itself uses it to disable the CAfile/CAdir. That way,
SSL_CERT_FILE=/a/path/to/a/cert SSL_CERT_DIR= command
can be used to ignore the native trust store. (This is how theopenssl
command appears to handle the env. vars.)
Let me know if you disagree with this plan.