sudo and su Being Rewritten in Rust for Memory Safety
phoronix.com
phoronix.com
sudo-rs: A memory safe implementation of sudo and su - https://news.ycombinator.com/item?id=35740863 - April 2023 (8 comments)
Bringing Memory Safety to sudo and su - https://news.ycombinator.com/item?id=35714347 - April 2023 (80 comments)
just a nitpick on their milestone4: https://www.memorysafety.org/initiative/sudo-su/sudo-su-work...
I understand mail capabilities, but LDAP/Kerberos direct integration? Why is PAM considered bad here? That looks like a duplication of maintenance points in user/group management
I think this "popularity" reasoning is flawed. It completely misses the context of the tool. Just ask yourself -- are there many UNIX CLI tools written in Ada/SPARK? I'm sure there are some, but not aware of any I use regularly.
Meanwhile, there is a Cambrian explosion of Rust CLI tools taking place.
"Popularity" is really a surrogate for "popular, accessible, infrastructure for an OSS CLI tool for UNIX", a place where Ada AFAIK has not been a first choice. If we were building firmware for a missile guidance computer, maybe the reasoning would be different?
But it's still not a sufficient reason, so I'll add another: People really like using Rust to make these types of tools. It's amazing to me that people are so willing to discount as non-technical, and therefore unworthy of discussion, lots of people really like using Rust.
Computer languages are partially to communicate between people and computers, and partially to communicate among people.
Whatever optimizes for the accuracy of the instruction output is the best. Sometimes it means language features. But it can also mean its popularity so you can get many more human eyes on the same bits of code.
But more importantly, how many of them are contributing to open source is lower (or at least advertising that they know Ada & contributing)
In a safe language like Rust, the level of capability you get doing even say the first couple of weeks of AoC is enough that your small contributions to a project are likely to actually work and be more or less serviceable, so that's a huge help.
I love to see the broad move towards safer languages, but let's not forget that Rust is not the only safe language for system development. Plus iirc the model-checking ecosystem of Ada/SPARK is much more mature than that of Rust.
That's roughly the situation today with SPARK. Using Ada does not automatically buy you memory safety on the same level as Rust. You can get a lot further with SPARK, but the annotations are very cumbersome to write.
These are well-known problems in the Ada community. There are efforts underway to improve SPARK's ergonomics by adapting ideas from Rust, with the help of the Rust Foundation. Someday soon, Ada will be much closer to Rust's sweet spot on the safety/productivity graph. In the meantime, there are CVEs to patch.
It reminds me of when the Ruby folks wrote their YJIT in Rust and there were a few Qs which were like -- Q: "There doesn't seem to be a security reason to write this in Rust, why not use C++?" To which, a totally reasonable A is -- A: "We like Rust (and there is no reason to be miserable forever)!"
People almost never choose a language for one reason (except for the people who use Ada, I suppose -- the reason is the government requires them to do so). Memory safety is a great reason to learn/choose Rust, but Rust is much, much more than memory safety.
Moreover, "Why are you ignoring Ada?" is "Why didn't you write/don't you rewrite this in Rust?" by a different name.
I have no idea what the SPARK subset of Ada is. But even if it is superior to Rust in one aspect or the other, the fact that you will have a very hard time finding any developers capable of using it kills it as an option.
It's the kind of infrastructure project that doesn't really attract much interest, I doubt there would even be a single external contributor if it were written in some niche language.
Edit: seems the article confused me with the word rewriting when the source homepage and repository says reimplemention, which makes more sense.
(e.g., look at the bluetooth/hci interface on linux.)
It's bad to think on security by just using a language.
My c compiler does not connect to internet during compiling.
Do you not have to install said dependency from the Internet and start (at least part of) the compile over?
Rust's build system, Cargo, does however. And many C build systems do as well, e.g. https://cmake.org/cmake/help/latest/guide/using-dependencies...
ifconfig eth0 down
before compiling to avoid such issues.
Wouldn't it be the static or dynamic linked libraries for the particular program in question?
There is a crt.0 "C Runtime" object. This is code that is literally run "at runtime". When you execute a binary, the C runtime is the first thing executed to setup the stack and call main().
This is just a bit of assembly that runs before main(). It shares ~nothing in common with "runtime" languages like Python or Java where the runtime is alive during the whole program's execution.
The C run time does not handle floats or threads or anything like that. Software floats are dealt with by the compiler and threading is implemented as its own library (either in userspace or built into the kernel).
https://learn.microsoft.com/en-us/cpp/c-runtime-library/c-ru...
It doesn't matter what people think, rather the CS point of view of a language runtime is.
That library is the runtime.
C implementation without the respective runtime is what is called a freestanding implementation as per ISO C.
This was a good name at the time. Its a bit of code that gets executed literally "at runtime". When you run a program, this object gets invoked to setup the stack and call main(). Its just a bit of assembly.
Then, decades later, languages like Java overloaded the term "runtime" to refer to their interpreter / vm. This causes confusion to this day, evidently.
It's funny how rust is praised as secure without any code audit.
https://github.com/RustSec/rustsec/tree/main/cargo-audit
https://mozilla.github.io/cargo-vet/
Besides, every dependency is locked.
Rust improves or eliminates some issues, but that does not imply that other issues are magically solved.
Nobody is suggesting that a language solves all security issues.
It does have much better memory safety than C, which is the cause of the majority of security issues.
(0) https://prev.rust-lang.org/en-US/faq.html#does-rust-have-a-r...
"Rust is blazingly fast and memory-efficient: with no runtime or garbage collector" [0]
I guess it depends on what you mean by a runtime. Panic handlers and initialisation code is a pretty small runtime.
I assume the more unsafe rust used, the more the CVE numbers will appear like C and C++. People like to say unsafe means "I checked this and this is fine, compiler" and isn't a bad thing but I feel that's inaccurate since the whole point of rust is that people struggle to write safe code even when they are really checking. I feel unsafe rust is way more "I really hope this is right, but I have minimized it to just this area out of necessity, since it likely isn't."
I would like to see how much CVEs go down over time. The fact sudo is being rewritten suggests that even after long periods of use, security issues popping up is still a common-ish problem?
It seems to average out at a bit over 1 per year over the last 20 years, based on [1]; is that "a lot"? I guess?
It's worth pointing out that while there certainly are memory-related C-style issues, quite a few are logic errors because all of this is rather subtle once you start adding features beyond what e.g. "doas" does. For example the recent "Sudoedit can edit arbitrary files"[2] is a somewhat subtle interaction between sudoedit, environment variables, and flag parsing. Previously there was [3] and [4] from 2004 and 2010.
Will memory safety be a benefit? Of course it will. But I think these kind of issues are the real challenge here. A drop-in replacement for "sudo" is useful, but I have to wonder if it wouldn't be better to rethink the approach from first principles, taking 40 years of sudo lessons in to account. It's also surprisingly easy to shoot yourself in the foot with a wrong sudoers file, which is not strictly a security issue in sudo as such but also something that can probably be improved on?
That 2023 sudoedit bug has been in sudo since 1.8, released in 2011, and Todd has been maintaining sudo since 1994, and he's actually pretty good as well as experienced with this kind of stuff. Yet he still missed it, as did everyone else, for well over ten years. It seems there are far more structural problems in the entire approach that go well beyond "C is not memory safe". That's certainly an issue, but in a way seems almost banal compared to the deeper issues.
[1]: https://www.sudo.ws/security/advisories/
[2]: https://www.sudo.ws/security/advisories/sudoedit_any/
[3]: https://www.sudo.ws/security/advisories/sudoedit_escalate2/
That doesn't mean it's impossible to write correct unsafe code, it's just not as obvious as "trust me bro I know better than borrowck." You can't actually elide the invariants Rust upholds, you just have to take over from the compiler when it can't prove them.
(1) https://doc.rust-lang.org/reference/behavior-considered-unde...
If the goal is security, then there is more to it than just using a memory safe language. Otherwise the result of this, possibly unwittingly, seems performative.
given that sudo's security history has mostly non-memory-corruption/overflow faults, the big win would be refining/simplifying it, regardless of language.
For context, if anyone's curious, here's the dependency list:
clap = "4.0.32" libc = "0.2.139" thiserror = "1.0.38" glob = "0.3.1" sha2 = "0.10.6" digest = "0.10.6" signal-hook = "0.3.15" log = "0.4.17" syslog = "6.0.1" env_logger = "0.9.3"
Rust has a perfectly nice protobuf implementation if they want one, why would it become a C dependency for a Rust rewrite?
Double free, just this year: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-2732...
Use after free: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0493
Recent buffer overflows: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3156, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1863...
Buffer over-read: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-4399...
And sigils are symbols, not special values: https://en.m.wikipedia.org/wiki/Sigil_(computer_programming)
[workspace.dependencies] clap = "4.0.32" libc = "0.2.139" thiserror = "1.0.38" glob = "0.3.1" sha2 = "0.10.6" digest = "0.10.6" signal-hook = "0.3.15" log = "0.4.17" syslog = "6.0.1" env_logger = "0.9.3"
$ cargo tree -e no-build --prefix none | sort | uniq | grep -v sudo | grep -v '\*'
aho-corasick v0.7.20
atty v0.2.14
bitflags v1.3.2
block-buffer v0.10.4
cfg-if v1.0.0
clap_builder v4.1.14
clap_derive v4.1.14 (proc-macro)
clap_lex v0.4.0
clap v4.1.14
cpufeatures v0.2.6
crypto-common v0.1.6
digest v0.10.6
env_logger v0.9.3
error-chain v0.12.4
generic-array v0.14.7
glob v0.3.1
heck v0.4.1
hostname v0.3.1
humantime v2.1.0
io-lifetimes v1.0.9
is-terminal v0.4.5
itoa v1.0.6
libc v0.2.140
linux-raw-sys v0.1.4
log v0.4.17
match_cfg v0.1.0
memchr v2.5.0
num_threads v0.1.6
once_cell v1.17.1
proc-macro2 v1.0.54
quote v1.0.26
regex-syntax v0.6.29
regex v1.7.3
rustix v0.36.11
sha2 v0.10.6
signal-hook-registry v1.4.1
signal-hook v0.3.15
strsim v0.10.0
syn v2.0.11
syslog v6.0.1
termcolor v1.2.0
thiserror-impl v1.0.40 (proc-macro)
thiserror v1.0.40
time-core v0.1.0
time v0.3.20
typenum v1.16.0
unicode-ident v1.0.8This should be much less necessary for Safe Rust. The existing C sudo needs libpcre2, and OpenSSL neither of which are small - among other dependencies.
Some of these dependencies do use unsafe Rust in places, and so it's valuable that those places should be inspected carefully (and not only for sudo) - but many do not, humantime for example is entirely safe Rust. Is it possible it has a logic error of some sort? Yes. Is it likely it somehow introduces a security hole? Not really. A C equivalent could easily introduce a critical buffer overflow, use after free or similar but that's not possible in safe Rust.
I had no idea sudo even had the need for plugins.
Which raises the question, maybe there's a need for two different sudo implementations. One that provides the simplest possible implementation of the feature, and another one that provides fancy log server and plugin integrations.
Or are you worried about bugs in dependencies? Because it's not like C sudo doesn't have dependencies.
Would you prefer that they implemented SHA2 themselves rather than using the battle tested crate that everyone else uses?
Ease of auditing is debatable. Using shared popular libraries gives the benefit of lots of people using them.
Plus actual code audits are very rare and of dubious value. They're mostly useful for finding out how well written the code is rather than finding bugs. For that your basically want fuzzing.
I love Rust as a language, but one of the challenges with many projects currently written in it is that they follow the NPM model of using dozens of tiny dependencies for even trivial functionality. Luckily this seems to be limited to users of crates.io for now, and maybe things will change as the library situation matures.
That doesn't mean there are a thousand dependencies. Dependencies take far more than one line in a lockfile.