The first stable release of a memory safe sudo implementation
memorysafety.org
memorysafety.org
Thousands of developers and security experts have gone over it. So part of me wonders - how is it possible for a single dev team to totally reimplement it without unknowingly introducing at least a bug or two? Is there something to this Rust language which magically eliminates all chances of any bug being introduced?
apperently, yes... and its the type system, but granted
it's only 'memory safety' bugs... the kind of error that C languages are really suceptible to.
It's so great to be able to listen and learn from the people that invented these important building blocks themselves, I feel lucky. Thanks for sharing.
`su` is straightforward, `sudo` is a very powerful piece of software and the configuration has a lot of edge cases.
The sudoers grammar will be described below in Extended Backus-Naur Form (EBNF). Don’t despair if you are unfamiliar with EBNF; it is fairly simple, and the definitions below are annotated.
OpenBSD replaced sudo with their own "doas" command a few years ago; the doas.conf manual page is about 100 lines; sudoers is over 2,000.
The thing is, replacing from C (sudo or anything else), the number of exploit due to null pointer or buffer abuse or ... represent easily 50% of it.
For sudo you have potential for some very serious logic bugs, where the program does exactly what the programmer wrote, but what they wrote was not what they intended.
Rust's type safety makes it less vulnerable to these mistakes than some languages, but there is no magic. In C obviously a UID, a PID, a duration, an inode number, a file descriptor, a counter are all just integers. In Rust you could make all those distinct types (the "New type idiom"), and out of the box the Duration and the File Descriptor are in fact provided as distinct types. So, some improvement.
For various kinds of IDs you can do that in C, too:
struct UID {
int value;
};
A C compiler can pass these in registers to functions (https://wintermade.it/blog/posts/value-struct.html). So, performance impact should be zero.It may be not as nice as other languages, but it isn’t bad, either. If you use C++, it can be made a bit nicer, and you could also have such structs that you can calculate with.
struct GID gid = *(GID*) &some_uid;
Which will compile without issues or warnings by default. No belts or braces in this area."Nothing" is too strong. It does not solve logic bugs, but type systems stronger than C can solve some logic bugs too.
Even something as simple as having some concept of "private" and "public" and some boundaries between them can help. I'm writing some code right now in Go, hardly a super strong type system, but I've still put some basic barriers in place like, you can have a read-only view of the global state, but the only way to write to it is to a per-user view of that state, and the only way to witness the changes to the underlying value is through one of those per-user write handles. This eliminates a large class of logic errors in which one accidentally reads the original global state when you should be using the per-user modified state or vice versa. This is a rewrite of some older code, and this error is so rampant in that code as to be almost invisible and probably in practice unfixable in the original code. (Which was solved in practice by only every dealing with one user at a time, and if there was multiple users, it simply ran the process completely from scratch once per user. It tried to cache its way out of repetition of the most expensive stuff, but, the cache keys had some of the same conceptual underlying problems, so it just wasn't as good as it should be.)
You can't solve everything this way. Rust's stronger type system offers more options, but you can't solve everything with that either. But with good use of types, there are still classes of mistakes you can eliminate, and classes of other mistakes you can inhibit.
(There are some tradeoffs, though; with bad types you can alse mandate incorrect usage. But I think in the case of something like a sudo replacement we can reasonably assume fairly high skill developers and that there will be a lot of high skill oversight, as evidenced by the fact they've already sought out a third-party security review.)
Cowboy code might be common, but you don't have to do that. If using something C-like, C++ definitely gives you a lot more tools to write safe code (or hang yourself, up to you) though.
C++ is an option to obtain the sort of thing I talked about, yeah, but in 2023 you need to use something memory safe for something as important as sudo, and C++ on its own is not. C++ and a great static analysis tool would be the minimum I would consider acceptable, but there is something to be said for things like Rust that build the analysis all the way in to the compiler rather than relying on external tools, and then future Rust external tools can build on that even more solid foundation if even more assurance is needed.
Almost always you read configuration files at run-time (like sudo does) and change your behavior depending on run-time information - so you will have run-time errors.
In your case, a config object would be mutable inside the function that loads it from disk into memory, then read-only everywhere else by default.
It usually devolves into a lot of nested if-else and switch (match) instructions.
Not saying you haven't done that, just sharing my personal experience with it.
This led to a lot of unnecessary friction with the borrow checker while I was still getting to understand it.
Once I started always asking myself "does this really need to be a ref?", things became much easier. And this in turn revealed itself as a useful rule of thumb for keeping coupling between subsystem in check.
Then I was doing some C work and I realised I was asking the same question to myself about pointers too. And I found it could often reduce cognitive load down the line, because I'd end up in much fewer situations where I'd have to figure out "who should free this, and when?".
That's when I realised this cognitive load of keeping the borrow-checker happy was always there in C too. It was just more diffuse, and invisible until it blew up in my face. And when it did it was in the form in nasty runtime problems, not a helpful compiler error that happened before the code was even checked in.
But there are also differences, for example, errors must be absl::StatusCode, whereas enums in Rust allow for arbitrary error payloads.
Also don't discount ecosystem usage: everyone uses Result in Rust, abeseil isn't used by most things, and std::expected has its own issues (though I can appreciate how tough making those calls is) and only landed in C++23, so it's not as widely used as Result either.
To use a C example, if you add a new definition/variant to an enum, suddenly all switch statements over that enum will fail to compile (unless there is a default: branch).
This does eliminate a large swatch of logic errors, though by no means all.
1. Valgrind exists. It's not perfect, but it does arguably do a pretty good job as long as you're writing modern C. The biggest gap I'm aware of is that it can't really help you with global pre-allocated buffers. But I don't think that any language or tool can effectively protect you from information leakage if you're doing that sort of thing, not even Rust.
2. Memory-safe is not the same thing as secure. Programs written in memory-safe languages are rotten with security vulnerabilities, too. Rust's happening to be a memory-safe language that doesn't use garbage collection does not render it immune to this situation. It has some protections around concurrent usage of data that do add additional safety under certain circumstances (assuming you don't switch them off), but I doubt it's a panacea. I worry, though, that the Rust community's tendency to pitch this stuff as a security panacea could breed a culture of complacency that negates the advantages that Rust does bring to the table for systems programming languages. People tend to take unnecessary risks when they believe they're invincible.
You may be right on an infinite frictionless plane, but unfortunately that does not work in real life, cf. e.g. https://msrc.microsoft.com/blog/2019/07/why-rust-for-safe-sy...
> Memory-safe is not the same thing as secure.
And safety belts do not help you if your car is on fire, still it's better to wear it.
There is no excuse to not at least have bounds checking. This is one of the most basic memory safety problems and it's trivial to prevent.
Just preventing this small issue will prevent a non-trivial fraction of bugs. I don't have sudo's bug list on hand but I wouldn't be surprised if 25% or more are caused by buffer overflows.
So even if it doesn't prevent all logic bugs, it cuts out a pretty big chunk of the bug list.
>assuming you don't switch them off
You can't switch them off.
>Rust community's tendency to pitch this stuff as a security panacea
I've not seen anyone claim this so far.
Yet you still don’t the same level of confidence as a rust program that may have a small unsafe block in one corner of the code.
>It’s important to understand that unsafe doesn’t turn off the borrow checker or disable any other of Rust’s safety checks: if you use a reference in unsafe code, it will still be checked.
Unsafe rust basically just lets you use raw pointers, mutate static variables, use C-style unions, and do FFI calls, but otherwise it's exactly the same, and the safety checks are not in any way disabled.
The main thing is that pointers let you access whatever memory you want, and borrow checking the pointer value itself doesn't prevent this.
I don't think I would describe this as "switching them off", I would describe it as, "using raw pointers" or something along those lines.
There's no such thing as modern C. C code that's written neatly and meticulously looks the same today as it did 30 or 40 years ago, except for language changes such as the move from K&R declarations to function prototypes. C the language hasn't changed since 1989 except for minor things like mixed functions and declarations, the introduction of the long long type, restrict pointers, designated initializers, compound literals, and threads being in the standard library.
I think it's at least worth humoring. It probably shouldn't be shipping as a default any time soon, though...
no, altough it has features that prevent or reduce the probability of some types of bugs - one example of this being memory safety bugs. rust can't prevent logic bugs.
the rust reimplementation probably has more bugs than the original, but a theoretically better chance to achieve fewer bugs in the long run.
is rewriting mature linux infrastructure in rust a good idea? many people agree that no, it's probably not a good idea outside of special use cases.
Also, it sounds like your seasoned eyes would be valuable in reviewing this code.
In this case though, it's only a partial reimplementation: "Leaving out less commonly used features so as to reduce attack surface", which would complicate that approach.
This is because we can stand on those giants' shoulders and have the benefit of hindsight and not have to also repeat each and every of their blunders, and have better technology and learning methodology to boot.
So I presume if you yourself wanted to rewrite sudo from the first principles, you, with all your experience and knowledge already there, would spend a lot less time doing it, and it would be way cleaner and simpler.
So while I'm not dunking on your effort and experience, I'm just pointing out that it's not impossible to take your experience and turn it into something better over a smaller timespan.
https://blog.qualys.com/vulnerabilities-threat-research/2021...
Would Rust prevent this?
For performance-insensitive, security-critical code, there really shouldn't be any such code in the entire program—and it would be easy to verify that with a presubmit.
This does imply that some of the stronger claims about Rust's level of static safety guarantees that float around on the Internet can't really be true unless substantially everything you might want to do has a version that's been completely written in Rust. Whether you feel that means that achieving the desired level of safety implies you've still got to rely on some dynamic analysis tools just to be sure probably depends on how much safety you really want, and how much faith you're willing to place in the skills of the authors of the libraries you use.
And even then, if we really want to go least common denominator, if you're running your program on Windows or a Unix or basically any other OS that isn't Redox, then you've got unsafe code executing every time Rust's own standard library needs to make a syscall to achieve something.
Which I don't say by way of criticizing rust Rust. It's got to live in the same crappy world we all have to live in, and it's arguably doing a better job of de-crappifying it than any other systems programming language. I'm just trying to illustrate how an unqualified statement along the lines of "there shouldn't be any unsafe code in the entire program" is kind of a self-strawman, precisely because Rust has to live in said crappy world, and I think that it might be unsafe to lose sight of that fact.
The borrow checker is intended to protect against "temporal memory unsafety". It can tell you that you are using something that has already been freed, or something that could be freed while you are using it for example.
Bounds checking is a "spatial memory unsafety" problem, it has nothing to do with borrowing and exclusive references.
Bounds checking is a trivial problem, for an array that has N length, like a char[N], something tried to access a value past the end of the array (like char[11] if N=10).
Rust doesn't really do anything special here and protecting against buffer overflows does not require any novel technology.
An implementation of bounds checking is as simple as an "assert(I >= 0 && I < N)", where I is the index and N is the length of the array.
In C this is difficult to do because arrays or are just pointers (or decay to) and pointers do not carry any information about the length. Keeping a separate variable containing the length around but this apparently is too unergonomic since virtually all C software does not check every array access in all parts of the program.
In Rust, its very rare to use raw pointers to work with "arrays". Instead there is a "slice" type that models the concept of a contiguous sequence of values in memory. The important part is that the slice type is a "fat pointer", and the fat pointer contains the length or the array. The slice type is able to check every access in all parts of program.
So all slice accesses are checked by default. and you can't "turn it off". If you really want to disable bounds checking for some reason, there is an unsafe "get_unchecked" function.
There are some sequence types other than slices, arrays for example (array is a specific type here). They are still checked but they don't have to store the length information because it's encoded into it's type.
The Vec type is another one. It is a resizable "array", and it stores 3 things, a pointer to the allocation, the capacity and the length. That's mostly not relevant here though. It checks every access like the other types.
Bounds checking is a very easily preventable error. It should not be happening in $CURRENT_YEAR.
This is often hard to say.
In a very literal sense, you could write this same code, in unsafe Rust, so one could argue that Rust does not prevent it.
Some may argue that if this program was written in Rust in the first place, "concatenate all command line arguments into one big string for processing" wouldn't be the way you'd go about escaping command line arguments. The issue here is about misplacing a null terminator, Rust strongly prefers a "start + length" style of representing strings instead of null terminators, so you'd never really end up in this situation in Rust in the first place.
I'm sure there's other ways to evaluate the situation as well. Which one you find compelling is up to you.
https://news.ycombinator.com/item?id=25919235 (321 comments)
Of course they did find and fix the bug, but in general Rust isn’t going to protect you from bugs like this that are essentially logic errors.
https://www.sudo.ws/repos/sudo/file/SUDO_1_8_6/doc/sudoers.m...
This is also a very common behaviour for security sensitive applications to check config file permissions. Another example I remember are ssh private keys.
I might be to harsh but it is not so trustworthy they still made this error and still miss the documentation.
The language it’s in is ‘mdoc’ - a markup format for man pages: https://man.freebsd.org/cgi/man.cgi?mdoc
It’s the source code for the man page, which is about as documentationey as you can get.
And I'm really sorry so many people have decided they're going to imply something is wrong or broken with it for their own clout. Or because they've bought into the lie that no code written in C can be safe or correct.
For what it's worth, I and all the engineers I willingly associate with (read: the ones who I respect) all have said the exact same thing. Switching to rust here, just 'cause, isn't going to meaningfully increase anyone's security. But what are you gonna do. Other than ask people to be honest?
Annoying fanboys aside... again, *thank you*! The computer security world is meaningfully better because of your work, and that's something the RIIR fad will never be able to replace :)
This is possible if every bug fixed has an associated test. If they use this battery of tests to test their new implementation it should be as good as the original implementation.
For me personally, creating this Rust version allowed me to work on something that I would normally not be able to work on, given how I would not rate my confidence in writing relatively safe C code very high. If nothing else, at least we already found a few bugs in the original sudo because of this work. Despite the 43 years of bugfixing, such a piece of software is unlikely to ever be free of bugs, even if just for the changing surroundings.
Other than that, having some alternatives can never hurt, as long as we keep cooperating and trying to learn from each others work (and from each others mistakes).
I think you might be projecting something here, there's no evidence for for your assertions.
As someone with over three decades of C programming experience (so not as much as you), maintaining widely used stuff written in C for decades, that has recently switched from C and C++ as main languages for systems programming to Rust, I'd instead ask this: How is it possible, even given 43 years of working the problem, to create a program in C that does what it's supposed to, and only what it's supposed to?
But also, one of the answers from the article is "Leaving out less commonly used features so as to reduce attack surface". Most security bugs in sudo are in features I don't use.
Rust isn't just memory safe. It's also orders of magnitude harder to accidentally make other mistakes, such as race conditions.
- Leaving out less commonly used features so as to reduce attack surface
- Developing an extensive test suite which even managed to find bugs in the original sudo
Which are the most important aspects when writing any safety-critical code, even moreso than rewriting in Rust!
- Proven with Coq, a formal proof management system: https://coq.inria.fr/
See in the real world: https://aws.amazon.com/security/provable-security/
And check out Computer-Aided Verification (CAV).
Edit: Looks like their github readme outlines some of these limitations, https://github.com/memorysafety/sudo-rs#differences-from-ori...
As annoying as it is to have to update every sudo reference -> doas, it forces you to think about everywhere you're using it, rather than waiting to see what breaks and then trying to fix it.
In my scripts I never call sudo or doas. Instead, if the script needs to do something as root, I write the whole script so that it expects to itself be run as root.
And then when I want to run my script, I run it as root
doas ./somescript.zshIn my experience, and in my own scripts, it is better to explicitly check if you are being run as root, advise against it and exit (with maybe some break glass flags) and invoke sudo when escalated privileges are required.
You're taking a shortcut due to convenience and it's bad security practice.
It's that simple.
AFAIK sudo isn’t really tightly coupled to the kernel itself.
Except all exiting use of sudo ...
It's such an entrenched tool that I'm sure there a compatible replacement could be useful.
Personally I would appreciate someone to take on the mess that is PAM. It was much too complex from the start and it hasn't become better over the years.
That's a pretty general statement, and I'd say to that not really. It's very much a hobbyist OS. A few people use it at home as firewalls, a few small businesses maybe, but it's mostly hobbyists and developers.
> To say that it's a hobby project strikes me as very ignorant.
I mean, I've been familiar with the project for over 20 years, so I don't think I'm ignorant at all. The developers primarily make the OS for themselves and people with the same ideas and priorities.
> That said, it is not very easy to convince the developers that a function is missing because it's a pretty opinionated project, and they might not share the user's definition of needed functionality.
Right, the devs prioritize their own needs, and can do so because it's a hobbyist OS.
I have plenty of ideological problems with the design of the logs, but not actually ran into problems in the real world.
journalctl -f
On the flip side, now you get structured logging, efficiently-searchable logs over any of those fields, the ability to easily aggregate logs from multiple machines, the ability to accurately iterate over logs in processes without missing entries, and on and on and on.Logs as a database is wildly superior to logs as a plain text file, with virtually the only downside being that you need a specific program to tail them.
The journal can output to text files as well!
But this is exactly it, my only issue is the ideological one that there is no spec for the database. The implementation is the specification, so the only true way of building a reader of logs is to implement the journal. There are attempts to document the layout but if there is any difference then you can't submit a bug to get the layout corrected but the implementation isn't wrong.
I can think of plenty of other applications with bigger issues than that. But I think can still want the defacto log for Linux to have an official spec.
The cool thing is running the editor via my user, which loads my user's configuration/plugins, instead of the root user's.
:w !sudo tee %
Which I map to :w!!
Of course, if you’re not using Vim, you’re doing it wrong :)
Now that I think of it, not sure how sudoedit behaves wrt this cached auth.
It’s a pity this isn’t more straight forward to implement on Linux.
This should be doable with an XDG portal model, right?
Maybe one could make a sudoedit that opens a file in sudo process and then spawns a non-privileged editor process which inherits the file descriptor and is given the /dev/fd/ path on the command line, so it stays none the wiser about the whole process.
For a more complicated solution: spawn a zygote process early with a unix socket which you’ll use to send the fd later. Zygote at start drops provileges. When it receives the fd, it closes the socket and execs the editor.
There is the CLOEXEC flag which is the intended way to manage this but it’s not the default and you have to be diligent about setting it which again carries its own set of challenges.
What you’d really want is CLOEXEC implicitly on all fds and having to explicitly opt in for fd inheritance.
I think the larger issue is that I doubt many (if any?) editors allow opening a file via an inherited file descriptor! I guess some will read stdin (the shim could close stdin and then dup2() it into its place), but then there's no way to save the file back when finished.
However, I’m not sure that Linux is capable of masquerading as a general purpose capability based operating system. I think it’s missing a bunch of APIs.
They indicate that many omittted features are by design, but this particular one is implied to be planned:
> Some functionality is not yet supported; in particular sudoedit
Sometimes reimplementing something and leaving out lesser-used features to "reduce the attack surface" can sound an awful lot like "let them pound sand".
I'm sure for someone out there that's a make-or-break feature, but for the vast majority trying to use OpenVPN it's a massive, insecure footgun. (Hell, how many bugs have protocol negotiation led to in OpenVPN/SSL/etc?)
Compare that to Wireguard that just says... it's encrypted. Full stop. Carry on.
A lot of this tooling and technology was developed in a different era with different priorities. Security, and especially network security, was not such a huge focus 30-40 years ago. Priorities have shifted. The operating environment is a lot more homogeneous (when's the last time you dealt with a layer 2 protocol besides ethernet?), while the risk of poor security has grown immensely.
It's absolutely fair to critically evaluate these features and determine which can be removed to simplify and improve the products for the vast majority of users. If a small fraction of users stay on sudo, but the majority are able to move to a more secure option... that's a win. This is exactly what Wireguard provides versus OpenVPN.
The binary executable will be bigger, but disk space is a lot cheaper nowadays, compared to 43 years back.
I mean, is there any time where one more level of indirection failed to make everyone happy?
So, you may get a memory-safe su/sudo-rs, but those who distribute it in a binary form won't be obliged to show you the source code it was built from (potentially including some modifications).
(this post is just here to insist on the issue)
Free software projects should welcome multiple implementations and interoperability, because these are the mother's milk of free software. It's frankly incoherent, given values of free software, that a reimplementation of, for example, Unix coreutils (GNU) would find fault with a reimplementation of itself (uutils).
Notwithstanding how philosophically incoherent it is, a desire, now that Linux and free software have some market power, to be a bully back, to grasp for monopoly power, to play AT&T, is really distasteful. What's exciting about free software is not the artifact, Linux or coreutils or sudo, but that anyone can create new and interesting alternatives. That users get to make choices about which implementation to use. The existence of FreeBSD does not make Linux worse. It makes it better! The "solution" to an MIT licensed coreutils is a GNU licensed fork which is 10x better. Instead, we get complaints which amount to a kind of free software entitlement, an endless pissing and moaning about how other people won't do new things your way.
This is a major problem in the way that most normies view the GNU and the GPL. In the past, I may not have chosen the GPL for my own projects, but I'd be pleased to contribute to a GPL project. Now, I'd have a hard contributing to a GPL project, because of just how toxic this attitude (no other license matters but ours) is.
the GPL is not a perfect license, there is no such thing.
Legally -- implicit in your comment are unstated assumptions about what is copied from the implementation. Suffice to say -- I disagree that a so-called "clean room" implementation is always required re: software published on the internet, or that the act of simply reading GPL code taints one forever, re: that code. The reason why is the reason why reading a book does not taint one forever and prevent one from writing one's own book -- "You should have used a while loop instead of a for loop here, now you're going to copyright prison." See, and pay close attention to the "Merger Doctrine": https://en.wikipedia.org/wiki/Idea–expression_distinction
It's my view that copyright is a rather weak IP protection, and GPL-type advocates have misconstrued the law of copyright for years, especially "derivative works". Mostly because they've been misinformed about the law by mendacious FOSS leadership cough like the FSF and SFC.
Now I'd agree with you, if there is something especially clever/creative about the GNU implementation that the reimplementation copies, that might be an issue, but again, even then, it seems especially rich for a reimplementation to find fault with another reimplementation.
You might also take a look at another comment of mine re: some aspects of this matter: https://news.ycombinator.com/item?id=38164958
For instance, GNU and POSIX both publish their specs for the coreutils. If a coder were to take a look at the actual GNU code (which BTW is published for everyone to see), copyright law has a well trodden distinction between the idea and the expression -- that is, ideas are not copyrightable. If the "idea" simply amounts to what would be a more a detailed specification, I'm not sure there is a problem, like ... GNU uses this kernel facility for X. The problem would be vast amounts of "expression", especially "creative expression", directly copied and reimplemented in Rust. If the code is meat and potatoes, not 10xer galaxy brain fare ("I wrote a custom allocator which is suspiciously like the custom allocator implemented by GNU"), there shouldn't be an issue.
Think about what copyright to a play, or a novel, or a screenplay is. Now imagine a comment in the text/source: "This is how Toni Morrison did her characterizations in Beloved". This obviously isn't a copyright violation, unless you're copying the actual expression or a translation of the actual expression found in Beloved.
[1]: https://github.com/util-linux/util-linux/tree/master/login-u...
Regarding security, malicious actor could show you a different source code from what he distributes in a binary form. GPL or no GPL.
It's not really bad but it kinda defeats the purpose.
That's why hashes are published by distributors and checked by package managers, right?
It doesn't help against a malicious distributor, unless package managers also do a deterministic build themselves and verify that checksum from self-build binary matches the checksum published by a distributor.
Otherwise, getting a package to run on different architectures would be a ton of manual labor. At least Debian certainly has that automated.
Besides the huge security risk involved in... just distributing random binaries?
Many Debian packages already have reproducible builds, but not everything.
From Debian wiki:
> Reproducible builds of Debian as a whole is still not a reality, though individual reproducible builds of packages are possible and being done. So while we are making very good progress, it is a stretch to say that Debian is reproducible.
https://wiki.debian.org/ReproducibleBuilds
But the point is that this has nothing to do with the type of an open source license. GPL doesn't guarantee you that a build is reproducible and MIT doesn't prevent you from having reproducible builds.
[1]: https://github.com/util-linux/util-linux/tree/master/login-u...
Look at the FOSS UNIX like RTOS, none of them is GPL.
In the realm of IoT FOSS UNIX like operating systems, all the contendants are using a mix of Apache, MIT and BSD licenses, including the ZephyrOS sponsored by the Linux Foundation.
When the GPL generation is gone from the face of the Earth, it won't last long that UNIX-like OSes get another steward alternative to the Linux kernel, with a more appealing license to big corps.
Linux kernel isn't the first nor the last UNIX clone.
You mean {Free,Net,Open}BSD?
* `bash`
* `bc`
* `binutils`
* `bison`
* `Coreutils`
* `Diffutils`
* `file`
* `Findutils`
* `flex`
* `gawk`
* `glibc`
* `grep`
* `tar`
* `gzip`
* `M4`
* `make`
* `man-db`
* `man-pages`
* `procps-ng`
* `psmisc`
* `sed`
That's a reasonable start, but you also need, minimally, something to replace `pciutils`, `IPRoute2`, a bootloader, and an init system. For a close to expected experience, add in `TexInfo`, `XZ`, `ZStd`, and `bzip2`, plus `shadow` if you don't want passwords stored in plaintext.
POSIX doesn't dictate an editor, but you probably want something that can run in a terminal. Usually `cURL` and either `openssl` or `GnuTLS`, plus `bind-utils`, `ldns`, or something equivalent are there for actually using the network, something to replicate `gpg` functionality if you're going to install signed packages, and of course the package manager itself. Cargo is fine for Rust app developers, but can't replace an installer of system packages. You likely need an `ssh` implementation to replace `OpenSSH`.
I'm sure there's more I'm missing, but this is pretty close to what you'd get in a minimal server image.
If you're looking to fully get rid of C and not need a C compiler, though, Linux itself is a hurdle. You don't necessarily need a kernel quite as fully-featured, but you need something that at least implements the POSIX system calls. Just about every Linux distro I'm aware of seems to also provide Python and Perl these days as a whole lot of system utilities and build scripts use them. Presumably, rewriting all of Perl and Python in Rust is not feasible, so you either need some other interpreted scripting language good for system scripting that is written in Rust, or somehow make your shell a superset of POSIX but also much closer to a real programming language.
Don't underestimate the lift of replacing `libc`, either. It's not just the C standard library and interface to system calls. It also provides the linking loader that makes it possible to even run other programs, all of the locales and time zones, the system's name server, profiler, memory dumper. A whole lot of stuff.
ed is the standard text editor!
Seriously, better reimplementations are great. But weren't you shocked to read about all those weird sudo features? I mean, the normal stuff is very weird, subtle, and therefore fragile.
Anyone who uses sudo "deeply" should probably think about whether there are other ways.
Portability helpers for projects like these enable a frictionless change.
Is anyone doing that? I see a lot of claims of memory safety, but as far as I can see the project isn’t saying other types of bugs are for sure eliminated.
Just because the source is a logic error doesn't mean the end result can't be bad when combined with a lack of memory safety.
That's a pretty severe unsolved security issue.
There's also a related issue with TIOCLINUX and the paste functionality. (That will however be solved in an upcoming kernel version. I wrote the patch for it :-)
If anyone wants to apply that to their kernel, it's on https://lore.kernel.org/all/2023101158-esteemed-condiment-1d...
Have any of these ever been adopted by a distro as the default implementation? Is that something that might happen? I.e. I would never bother to upgrade my sudo command or my grep command, but getting better defaults would be better
Obviously they would have to be perfect drop-in equivalents, which some of these projects don't try to be, but others of them do
Multiple implementations make it much easier to do fuzzing and generate automatic test suites that may be used to improve all the versions of this critical utility.
In principle I think this is a great idea, but when it comes to encouraging adoption, I think this might be a mistake. I would love to use this, but I'm not going to install it myself and make sure it stays updated. Realistically, I'm only going to use it if Debian decides to replace their sudo-c package with sudo-rs. And I just don't see Debian (known for being fairly conservative with changes and updates) doing that when sudo-rs doesn't implement sudo-c's full feature set.
Sudo-rs' first security audit - https://news.ycombinator.com/item?id=38131442 - Nov 2023 (58 comments)
No thanks. Keep this far away from all of my systems.
Nothing a user is supposed to do should require elevating to root to do.
>it’s much better than just giving out the root password to users.
This is even worse. Access control should be set up properly instead of having users assume the identity of other accounts.
1. Don't. Just include the correct configuration as part of the OS.
2. For a configuration file use a group for passing out write access to users on the system.
3. Have a daemon which handles updating configuration files based off some central source of truth which pushes changes to a fleet of servers. Programs could directly communicate with this daemon to get their configuration.
Rust enums are sum types, and imho are one of the few unambiguously good language feature ideas. I miss them any time I use a language where they are not built in. F# is another nice language where they are first class and where I first got familiar with them
I wouldn't mind so much if they just called them "sum types" or "tagged unions", or even some other new name. Reusing the existing name "enum" from other languages, but differently from the way all those other languages have used it for 45 gorram years, is freaking maddening.
https://docs.scala-lang.org/scala3/reference/enums/enums.htm...
They're usually called 'variants'.