Maybe a good time to plug doas, a simpler alternative to sudo from OpenBSD folks[1], developed partly due to security fears about sudo. It’s also been ported to Linux and is available in e.g. Alpine and Debian.
https://github.com/sudo-project/sudo
On the contributors tab:
https://github.com/sudo-project/sudo/graphs/contributors
We can see that sudo has been maintained by one person for ~29 years who has single-handedly committed ~2,936,000 changes over that time resulting in a net increase of ~480,000. It is also being actively developed with ~270,000 changes resulting in a net increase of ~40,000 lines in the last twelve months.
In contrast doas is, just eyeballing it, maybe ~500 lines and at most 1000 lines all together.
It doesn’t remove a bit of how impressive the dedication of Todd C. Miller is of course.
These are two solutions with a common core goal but greatly different concerns.
I don't think it makes sense to add all added and removed lines together and call it "changes": for example, this counts even a single character typo fix as 2 changes.
Last time I checked there were several different ports floating around. Now I see at least Debian has chosen opendoas, I guess people have mostly converged to that one. Also fun stuff like https://xn--1xa.duncano.de/slicer69-doas.html
The point being that while openbsd doas is undoubtedly very nice, the linux ports are separate projects
But for simple systems that aren't running complex user privilege management, doas is very much a great replacement.
https://wiki.archlinux.org/title/Doas#doas_persist_feature
https://wiki.gentoo.org/wiki/Doas
With the persist keyword doas can remember an authenticated user and will not require confirmation by password for five minutes.
The biggest difference we found is that sudo is a little more convenient to manage with a config management solution, since each piece of config management code can just drop a file in /etc/sudoers.d. With doas, we have to coordinate a bunch of parts of the config management manipulating one file, /etc/doas.conf. This always turns out a bit messy. But maybe we'll just merge everything into one big and somewhat messy monster doas-conf-template with a ton of feature toggles and loops and go from there.
But beyond that, almost all of our sudo rules are 2-3 patterns: Admin can sudo as everyone, zabbix/telegraf can run data gathering commands, and maybe some other automation triggers like pgbackrest wiping a postgres data dir during a restore, that's about it. All of those are pretty trivial doas rules all in all.
Apparently whether it's used is configured at build time, because I get different results trying it on Arch and Alpine, but doas absolutely has a /etc/doas.d
EDIT: Oops, apparently Alpine added that - https://git.alpinelinux.org/aports/tree/main/doas/configurat...
If you don't need those features, doas is probably a good replacement. But if you do, it probably isn't an option.
Sometimes less is more. My immediate response to that is that it sounds like needless complexity which can be a source of errors, bugs and security-vulnerabilities.
What typical use-cases do these particular features have?
That you've heard about. There's two big reasons: (1) people often don't try to spend a lot of time finding exploits for userspace programs that don't open network ports or perform privilege escalation. And (2) when they do find exploits in those programs, they don't make it to HN.
https://slackbuilds.org/repository/15.0/system/opendoas/?sea...
It also contains some information on how to setup doas on Linux
libxml? zlib? PHP?
The déjà vs are many…
In practice, this means that if your password is only one char, then the actual buffer is two bytes long, and the seventh byte past the buffer is then zeroed/set to the null terminator. I wonder if and how this is exploitable.
When I see things like this, the first question I have is why? A password isn't going to be long enough to require dynamic allocation, so just use a fixed-size buffer. 255 is already generous and a good round number. The best solutions are often also the simplest.
I guess things are different in the Linux/PC software world.
Perhaps because they were still fighting the last war, where they were hurt by a greater-than-255-character buffer overflow vulnerability.
My default assumption is three letter agencies surreptitiously adding back doors.
https://googleprojectzero.blogspot.com/2014/08/the-poisoned-...
There have been other examples where only a 1 could be written.
Well, looking at the upstream code, the original value is saved away before zeroing it out and then unconditionally restored after running crypt.
If sudo is single threaded and crypt() doesn't malloc() anything, I don't think that can be exploited. Worst case would be a segfault if the password was somehow close enough to a page boundary.
glibc malloc() should be aligned to 2*sizeof(size_t), so strup("")[x] on 64-bit systems (with 16-byte alignment) can never crash or overlap another object where x<16
On 32-bit systems and with other mallocs you could potentially be reaching another page (like I think you are imagining) or trashing some bookkeeping bits which might crash free() but I cannot yet see how you would induce that, nor convince myself it cannot be done without spending more time thinking about it (something I'm reluctant to do with my afternoon)
Would be at worst a memory read past the inputted password, which wouldn't be super useful outside of a leak for another vulnerability but even that seems unlikely.
So it's not just reading past the end of the buffer, but it's overwriting a single byte potentially belonging to another object. It may still cause a crash but it's relatively unlikely that it could cause something more severe.
The first question that we all want to ask
Could it be mitigated by safer, modern tech?
One does not even need to reach for Rust. Literally any other language (in common use) other than very badly used C++ would not have had this problem.
C delenda est.
Is it that GNAT only became available relatively late in the lifetime of GNU/Linux? Or is there another technical reason for it?
> /*
> * Truncate to 8 chars if standard DES since not all crypt()'s do this.
> * If this turns out not to be safe we will have to use OS #ifdef's (sigh).
> */
> sav = pass[8];
This bug was introduced by the latter.
If you seriously think a buffer for a password needs to be dynamically allocated, the "safest" and most "modern" shit won't help. It'll just contribute to the ongoing decline.
This was already a solved problem in NEWP, JOVIAL and PL/I, no need for modern tech, only not to insist in using broken by design one.
Like... Coverity Scan?
In my experience, this bug looks like a classic example for something that Coverity should find.
And it looks like sudo is already on there: https://scan.coverity.com/projects/sudo
The last analysis was 2 weeks ago. I wonder if this CVE is among the outstanding memory corruption and illegal access defects (5 each).
People use fucking java in HFT
dont worry, our basic tools are fine with Rust, go or even c#
Bounds checking has been standard in every language other than C since 1970, but for some reason C programmers refuse to use it, normally using arguments like "just make sure the indexes are correct", which is basically "just don't write bugs".
Are you going to rewrite all of that in $other_language_than_c? How many hours of work do you think this will take to rewrite?
This is the real issue.
I'd argue that's (at least part of) the problem. More code = more surface area for bugs, and sudo has a lot of code.
Surely that can‘t be that much in the name of security, no?
sudo make install, ok, great, some of the many operations you need to do requires privileges? Better give elevated privileges to all operations!
Even worse with GUI: enter your password to install. Now I have absolutely no clue what the scope of sudo is.
Of course I don't want to enter my password for all individual cp and mv operations, but if sudo had a better/smaller scope that'd be great.
On the other hand, having to always explicitly specify all the fine grained capabilities a process might need is a pain, too.
Files within /etc do, for security reasons, but there's no reason why you couldn't use user groups or other ACLs to secure those folders.
chown /etc to nobody:wheel and chmod it to g+rwx; users in group wheel will now be able to manage /etc. You've got to make sure you set your umask right if you do use sudo for /etc again, but that's also just part of your system configuration.
Doing fine-grained access is really hard; even without "root" you still have things like, say, "archive_command" in postgresql.conf which will allow running people to run arbitrary commands as the postgres user, and is that really what you want? There's lots of little things like that ranging from application configurations to crontabs to your init system.
But these days many computers are only used by one user.
Everything I care about on my computer is readable by my user and a program running as my user could put fake binaries in my path.
Flatpack et al. have improved this situation somewhat, but come with their own drawbacks. Linux needs a central application-level permission system like Android, where I can grant/revoke e.g. internet access to applications. Frankly, I should never have to use sudo to install anything in my daily life, that is unfortunately not the case with the common ubuntu install, and will probably stay this way for a long time.
Now on a server, sudo for a single user probably doesn't make sense, just use root and keep it simple.
B - Not sure how practical most of this is yet, but there's cool stuff around isolating individual programs even on single-user machines.
C - My desktop has a couple things that listen on the network, and it's nice that they only have access to specific things.
For example, sound demon like pulseaudio runs as your user (...for some reason, fucking Lennart) but it really should not have write access to anything aside from its own config and for 99,99% users also not have access to read anything your user owns aside from its own config.
Even browsers should probably be limited, or user should at least get prompt, there is little reason to allow browser to dig around your system willy nilly, let alone in locations like ~/.ssh
For installing things, you generally need write permissions to /usr/bin and likes. So you could create an user with such privileges and sudo to that.
The real issue, I think, is Linux not being capability based, so there's no programmatic way for scripts to communicate which sort of permissions are needed.
OpenBSD has something like this https://man.openbsd.org/pledge.2
I see this attitude in pentesting too on embedded systems. A developer encounters a problem they don't quite understand but the problem disappears when they run their app as root, so away we go.
That's a part of a reason for its complexity it does allow you to do anything between "make user be another user with all priviledges" to "just allow to run this particular command and nothing more".
Having one that had option for more limits would be interesting (say use cgroups to change running user but disallow command from modifying anything aside from this one single directory you specified) but, well, that's way more code that also needs to be secure...
Even ignoring security issues, all of make getting elevated privileges can cause other issues as well.
All it takes is the incremental build system being a little finicky, and "sudo make install" could rebuild an object as root, and now one or more of your ".o" files are owned by root and your build directory is broken.
If you are in sudoers and you are compromised, then there are like a million ways of getting root for a malicious program. They could override your sudo, override your terminal, override your shell, override your de, even override say "cat" so that instead of exiting when it is done, it starts a shell that mitms all your commands and waits for a sudo one.
<https://access.redhat.com/security/cve/CVE-2022-43995>
Description:
... Sudo 1.8.0 through 1.9.12 ...
Statement:
The sudo package as distributed with Red Hat Enterprise Linux 7, 8 and 9 is not affected by this issue as it currently doesn't ship the affected code.
<https://access.redhat.com/downloads/content/sudo/x86_64/pack...> 1.9.5p2-7.el9
1.8.29-8.el8Got it. Linux distributions (ex. RHEL) have --with-pam in configure, so not vulnerable (code not compiled). (If you have --with-passwd in configure, then passwd.c is compiled, and you are vulnerable, but Linux distributions do not do this.)
<https://ubuntu.com/security/CVE-2022-43995>
sudo packages in Ubuntu are compiled with PAM support, so the vulnerable code isn't part of the binaries.
Not vulnerable (code not compiled)Zig, Nim, Rust, D, V, whatever -- can't we just move on from C/C++ already? It's obvious they are not up for the job.
And even if modern C++ is amazing -- I have no doubt it's improving all the time -- that doesn't change the fact that there are metric tons of C++ code out there that nobody will ever modernize. So C++ getting improved with time is sadly an almost moot point.
I'd personally advocate for Rust but I've heard people say there are a few other languages that allow you to achieve the same memory safety so, by all means, let's please start rewriting and make our everyday tools something different than a Swiss cheese of potential and actual security threats. It really is time.
The language doesn't matter, the outcomes do. And in terms of outcomes I maintain that C and C++ have not stood the test of time in terms of security and amount of foot guns. Too much sentimental value is attached to them as well and that doesn't help matters either.
We work with tech. We don't make love with it. We should all start acting like this is a job and not [only] a hobby.
Why can't we just have a minimal version of sudo that does just that and only that so the majority of smaller servers and home users can run sudo without fear of a security bug ever other month? Preferably using the same executable path so that everything else doesn't break.
It just seems like most of sudo's security bugs come from weird obscure features almost no one uses. Like that time sudoedit had a security issue. I didn't even know that command existed until it broke things, and it still seems pointless when you can just run "sudo nano" or "sudo vi".
Lessons:
* Serious bugs doesn't care in which language the error happens
* C++ implementation was safe
* Java implementation was unsafe
* Test-Coverage would help...
PS: I don't say Rust is good/bad. C++ is good/bad. Or is good/bad. Neither about Java.This just isn't true.
Buffer overflows are not possible with bounds checking.
Using a language that provides containers with bounds checked access methods would have prevented this. This isn't a point of debate or something, it's a fact.
C is virtually the only language that doesn't provide a safe way to access elements.
C++ provides bounds checking with std::array, std::vector and std::string using the "at()" methods. All Rust containers are checked by default. Pretty much every other language also is checked by default as well. All of these language's could have prevented this error and the other buffer overflow errors which there are tons of.
Yes, given enough care and effort you might write code that will not have those bugs, but not having a possibility (aside from unsafe{}) to have them in the first place is usually better approach.
Like, yeah, it is a dumb meme but in this case not without merit.