Where does Cargo hard-code the CA paths?

Hi everybody,

complete newbie here. I'm looking for some info on how Cargo determines what the paths to look for the CA store are. Information on that matter seems to be a little scarce in the Cargo book. It simply mentions that if http.cainfo is not set, "Cargo attempts to use the system certificates".

What I need help with may be a bit of an unusual question, so here's a little background on why I'm asking in the first place:

I'm on a team that is concerned about the general trend of de facto monopolization in the IT world. For example it's a well-known fact that there are very few viable browser engines left. While the situation is a little better regarding operating systems, the "big three" still get most of the attention. We believe that platform diversity is very important for various reasons like the resiliency of modern tech (e.g. because more exotic platforms are known to help exposing bugs and other issues early before they strike much harder later).

Well, we happen to also like the ideas and values of Rust - and while we did some work on better supporting *BSD in the os_info crate, it's of course even more important to make Rust easily available on various non-mainstream platforms first. One project that we are contributing to is a cross-platform packaging framework for Unix-like systems. Together with a lot of other software it provides Rust in packaged form on DragonFly BSD, FreeBSD, Linux (glibc-based) and NetBSD currently. It is designed in a way so that it doesn't interfere with whatever the system's main package manager is; to do that, it uses a custom prefix (think installing software to /opt). Since this means that libraries, headers and such are all installed in a non-standard location, some software needs to be patched to work.

Cargo currently requires to set the CARGO_HTTP_CAINFO environment variable or it will error out with a "SSL certificate problem" as it cannot find the certificate bundle. While setting this does work, it's rather inconvenient and not the most pleasant experience for the end user. For that reason I would like to teach cargo to find the certs by default by applying a downstream patch when we build it. Unfortunately I have no idea on where to look for that (my attempts at grepping through the source did not yield much). Any hints or pointers would be most welcome.

On my Arch Linux desktop, it looks like it uses /etc/ssl/cert.pem.

$ strace cargo search tokio 2>&1 /dev/null | grep -E '\.pem|\.crt'
statx(AT_FDCWD, "/usr/local/share/cert.pem", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/certs.pem", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/certs/ca-certificates.crt", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/certs/ca-root-nss.crt", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/certs/ca-bundle.crt", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/CARootCertificates.pem", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/usr/local/share/tls-ca-bundle.pem", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7fff54248d30) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "/etc/ssl/cert.pem", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0444, stx_size=213461, ...}) = 0
openat(AT_FDCWD, "/etc/ssl/cert.pem", O_RDONLY) = 3
openat(AT_FDCWD, "/etc/ssl/cert.pem", O_RDONLY) = 6

From there, I ran cargo under GDB and told it to break on the open syscall when it is trying to open /etc/ssl/cert.pem.

$ gdb $(which cargo)

(gdb) break open if strcmp($rdi, "/etc/ssl/cert.pem") == 0
(gdb) run search tokio
...
(gdb) bt
#0  __libc_open64 (file=file@entry=0x555556621fee "/etc/ssl/cert.pem", oflag=0) at ../sysdeps/unix/sysv/linux/open64.c:30
#1  0x00007ffff7c84b06 in __GI__IO_file_open (fp=fp@entry=0x55555661ed80, filename=filename@entry=0x555556621fee "/etc/ssl/cert.pem",
    posix_mode=<optimized out>, prot=prot@entry=438, read_write=8, is32not64=<optimized out>) at fileops.c:188
#2  0x00007ffff7c84ccb in _IO_new_file_fopen (fp=fp@entry=0x55555661ed80, filename=filename@entry=0x555556621fee "/etc/ssl/cert.pem",
    mode=<optimized out>, mode@entry=0x55555612eb9d "r", is32not64=is32not64@entry=1) at fileops.c:280
#3  0x00007ffff7c78f2f in __fopen_internal (filename=0x555556621fee "/etc/ssl/cert.pem", mode=0x55555612eb9d "r", is32=1)
    at iofopen.c:75
#4  0x0000555555e651e3 in BIO_new_file ()
#5  0x0000555555ef8456 in X509_load_cert_crl_file ()
#6  0x0000555555ef85ec in by_file_ctrl ()
#7  0x0000555555ef91c6 in X509_STORE_set_default_paths ()
#8  0x0000555555c8cb40 in git_openssl_stream_global_init ()
#9  0x0000555555c6d7c4 in git_runtime_init ()
#10 0x0000555555ca6f15 in _RNCINvMs2_NtNtCseYaabqaK1dc_3std4sync4onceNtB8_4Once9call_onceNCNvCs9uIQkBdaQHf_11libgit2_sys4init0E0B12_.llvm.4158767007341341918 ()
#11 0x000055555558d83a in std::sync::once::Once::call_inner () at library/std/src/sync/once.rs:434
#12 0x0000555555c24bd7 in libgit2_sys::init ()
#13 0x0000555555c1db2f in <git2::config::Config>::open_default ()
#14 0x000055555575acd8 in cargo::ops::registry::http_proxy ()
#15 0x000055555575a059 in cargo::ops::registry::needs_custom_http_transport ()
#16 0x0000555555599ee4 in cargo::init_git_transports ()
#17 0x00005555555d2eb9 in cargo::cli::main ()
#18 0x000055555559808e in cargo::main ()
#19 0x00005555555e00f3 in std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()> ()
#20 0x0000555555605f49 in _RNCINvNtCseYaabqaK1dc_3std2rt10lang_startuE0Csi9W78wegHfQ_5cargo.llvm.18090902285484755069 ()
#21 0x0000555555fb715e in core::ops::function::impls::{impl#2}::call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> ()
    at /rustc/c52b9c10bfb5164015eb977ff498e0597ae63eb1/library/core/src/ops/function.rs:280

So it looks the actual certificate access comes from the libgit2-sys crate. Hopefully that backtrace will give you enough information to figure out what code needs patching.

1 Like

That is for cloning git repos. For downloading crates cargo uses libcurl, which should look at the same places.

1 Like

Thanks, I think I've found the right place to patch! Will try it out when my build server is operational again. Will mark this solved afterwards.

It uses openssl-probe, which looks in a variety of places used by different Linux distros: openssl-probe/lib.rs at master · alexcrichton/openssl-probe · GitHub

2 Likes

Thank you very much for pointing me there, this turned out to be the perfect solution! Sorry for replying so late, but I can confirm that the local patch is in now and the new packages will probably be shipped, soon. :slight_smile:

1 Like

Have you considered wrapping your packaged Cargo in a shell script like this?

export CARGO_HTTP_CAINFO='...'
exec '/path/to/real/cargo'

I say this as a user of NixOS, which installs files at very nonstandard paths and uses such scripts routinely.