rustls-native-certs icon indicating copy to clipboard operation
rustls-native-certs copied to clipboard

FreeBSD certs not found

Open LukeMauldin opened this issue 2 years ago • 5 comments

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.

LukeMauldin avatar Sep 14 '21 21:09 LukeMauldin

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.

LukeMauldin avatar Sep 14 '21 21:09 LukeMauldin

Should all possible locations be searched and aggregated?

Ralith avatar Sep 14 '21 22:09 Ralith

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.

LukeMauldin avatar Sep 15 '21 01:09 LukeMauldin

Would you mind submitting a PR for aggregating? It seems reasonable to me.

djc avatar Oct 11 '21 08:10 djc

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?

charlespierce avatar Feb 02 '24 21:02 charlespierce

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.

pgerber avatar May 08 '24 22:05 pgerber

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.

pgerber avatar May 09 '24 01:05 pgerber

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?

djc avatar May 09 '24 11:05 djc

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?

ctz avatar May 09 '24 14:05 ctz

@pgerber want to submit a PR for this?

djc avatar May 12 '24 09:05 djc

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.

pgerber avatar May 12 '24 20:05 pgerber

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.

ctz avatar May 13 '24 08:05 ctz

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 the openssl command appears to handle the env. vars.)

Let me know if you disagree with this plan.

pgerber avatar May 13 '24 11:05 pgerber