Do rustc and cargo have checksum and signature mechanisms to prevent modification of locally checked out std and third party crates (from crates.io) source code?
No, after download there is no check in place to ensure integrity of caches. If malicious code can modify the source code, it could likely modify the compiled artifacts too.
But except for malicious behavior, there are other situations that can cause accidental changes to the checked out source code, such as hard drive and file system failures, or even accidentally hitting the keyboard and saving while browsing code after a jump through intelisense.
There are also crates whose
build.rs explicitly edits their own source files (non-maliciously, but it can cause issues depending on what exactly they do). I've long wished that
cargo would make
$CARGO_HOME/registry/src/ read-only during builds somehow, but the only prototype I've been able to get working reliably is using something like bubblewrap to do remounting of it which seems like too much overhead to integrate into cargo directly.
It depends what exact scenario you imagine.
If someone malicious gains control of crates.io servers, they will be able to push new crate versions to the index or intercept and modify crates uploaded by others, and these crates will be fully trusted by cargo, as they will have correct checksums in the index.
OTOH if someone only gains access to place where crate tarballs are stored, they won't be able to change any crates, because the tarballs must match checksums in the index. Similarly network access is protected by HTTPS and the checksums, so network MITM is not a threat.
Cargo still verifies the checksum with
Cargo.lock AFAIK. So either the crate already existed in
Cargo.lock and Cargo complains about the mismatch when attempting to download, or the malicious checksum will be checked into a VCS and the next time someone else builds it (eg on CI) Cargo will complain.
If there's a new version added, you're only
cargo update away from compromise. Never updating any dependency ever is not a practical safety solution.
As of Linux 5.13 there is the Landlock LSM which would allow restricting access to
$CARGO_HOME/registry/src/ to read-only. This works even as unprivileged process. The only limitation I know of is that you need to use
PR_SET_NO_NEW_PRIVS to prevent spawning setuid binaries. They even provide a Rust crate: GitHub - landlock-lsm/rust-landlock: A Rust library for the Linux Landlock sandboxing feature
Or your editor dumping you into the cache when some warning happens from them (usually proc or "normal" macros IME), you don't notice, and then things seem awfully strange from there.
For this situation, and to mildly discourage people from editing what is supposed to be a cache of immutable objects, it would be nice if Cargo set the ordinary file permissions to be read-only after downloading.
I agree, though apparently that breaks some
build.rs code . Can
cargo detect this in any way and start warning at least?
So cargo doesn't have any signature (rather than just integrity checking) mechanism?
Also I actually take the issue about build.rs security very seriously. After all, this is a full-featured, full-access executable that could theoretically (even if the cause is careless coding) delete the user's files or do other things that can be dangerous. Major incidents about build scripts like this have happened. Any more discussions and proposals on this?
Note that proc macros have all the same security problems that build.rs files do, and sometimes that full filesystem access is an important feature of how they work as well. The key things to protect yourself are to a) don't depend on crates that you don't trust, b) never run cargo with elevated permissions, and c) if you ever do manage to find a malicious crate, report it asap.
Alas, I've found that CI systems running containers tend to assume in-container
I think you could use
*.runner to separate out
build.rs and proc-macro permissions (e.g. a dedicated user) from cargo operations more generally (but I didn't check that it actually covers everything).
Or you could wrap cargo itself.
Or you could do things at a higher level in your CI environment, like
cargo fetch, secure the downloaded files however you would like, use
cargo --offline elsewhere...
More generally, I suggest your dev and build environment for any modern language always be at least minimally sandboxed, e.g. a dedicated user with minimal permissions (cannot read your personal files) though I don't know to what extent the general community agrees. It may be a pain to do so with some IDEs or OSes.
If you don't trust a package's
build.rs, you probably shouldn't trust its source code either (which can also end up running in your environment... via compilation into your binaries).
I tried publish poisoning-rustc.
Compiling crate whose dependencies contains this one would modify rustc.
I always wanted to see cooperation between cargo and built.rs. You have to put into the Cargo.toml how much privileges the build.rs needs. (a) cargo can give the correct sandbox and (b) you can search for build.rs files with high privileges.
That's probably not the end of the world. Containers are a bit of a sandbox already, even if it requires some special configuration to make them truly secure. Additionally, malware will probably make its presence known in your dev environment before it winds up in CI, and even if it doesn't, blowing up a CI runner is probably the best-case scenario if you accidentally depend on a malicious crate.
I know I've personally been guilty of running cargo in an admin terminal before when using cargo flamegraph (dtrace requires elevated permissions), but that's only with trusted dependencies with an application I've already built and tested and am confident won't break my computer.