
Thoughts on the Systemd Root Exploit - uggedal
https://www.agwa.name/blog/post/thoughts_on_the_systemd_root_exploit
======
blakesterz
So he closes with

> Unfortunately, the lock-in they're creating will deprive people of the
> ability to vote with their feet and switch to better alternatives.

This might sound like a dumb question, and I'm not saying I agree with him,
but if I did, I'd vote with my feet by... what? Don't most distros use it now?
I'm genuinely curious if this is a practical option for me, though I'm not
likely to do it.

~~~
Sanddancer
FreeBSD, OpenBSD, NetBSD, Illumos, etc. Chances are, you don't even need to
tie yourself to the Linux kernel. Lots of OSes out there that don't inflict
their users with systemd.

~~~
gribbly
If you don't like systemd due to it being a lock-in, why on earth would you go
with the BSD's who are all lock-in's with their respective implementations of
lower-level plumbing which systemd is trying to standardize across Linux ?

~~~
Sanddancer
The BSDs have far less lock-in with the default components than Linux
distributions usually do. For example, I dislike the default syslog daemon,
so, I disabled the default, and used my own. This involved adding two lines to
/etc/rc.conf --

    
    
        syslog_ng_enable="YES"
        syslogd_enable="NO"
    

and that was it. Unlike systemd's way of doing it, the default syslog facility
isn't started at all, doesn't run, period. Pretty much all the other low level
services -- crond, dhcpd, ntpd, etc -- are the same way. This is one of the
many failings as to how systemd operates. One should have reasonable defaults,
but at the same time, if the defaults don't work, it should be easy to change
them.

~~~
gribbly
Well, journald is pretty much the one component of systemd that is very
difficult to replace, but even so you can easily forward to another syslog
daemon of your choice.

As for the rest, it's very much the same as with the BSD's, they all support
their own versions of low level components and only them, and these can be
changed by the user with compatible tools of their choice.

~~~
digi_owl
Forwarding to another process still means that if Journald goes belly up, the
logging just died...

------
JdeBP
> _systemd was using a magic value (-1) to represent an invalid mode_t value_

This is not a correct description of what happened, note. Kay Sievers
originally used 0 to represent an invalid value. Lennart Poettering changed
this to -1, because 0 is clearly not an invalid value. The bug resulted
because _he missed changing one of the comparison-against-zero validity
checks_.

See
[https://news.ycombinator.com/item?id=13472516](https://news.ycombinator.com/item?id=13472516)

~~~
agwa
> The bug resulted because he missed changing one of the comparison-against-
> zero validity checks.

Yes, that's exactly the kind of mistake that happens when you use magic values
to represent invalid values instead of distinct types.

~~~
JdeBP
You still have not got it right. This is an _old_ class of problem, documented
since the 1960s. It's _nothing to do with types_. It's _nothing to do with
what language one uses_ (since it has been documented in languages as diverse
as RPG and Pascal). It's the use of unexplained constant literals.

M. Poettering was in fact doing the right thing and _correcting the problem_ ,
replacing the unexplained constant literals written by M. Sievers ("mode > 0")
with named constants ("mode != MODE_INVALID"). It is an example of the
problem, one of whose symptoms is the question "Well which of these is the
specific constant and which just happens to also be that number?", that M.
Poettering missed a "mode > 0" that also needed replacing.

Make no mistake. M. Poettering was actually applying the long-time well-
understood fix for this.

Here's Ted Holt talking about unexplained constant literals in RPG 4:

* [https://www.itjungle.com/2004/08/18/fhg081804-story01/](https://www.itjungle.com/2004/08/18/fhg081804-story01/)

This same problem, and the approach of turning unexplained literals into named
constants to improve maintainability, is explained all over the place, from
William Allan Wulf's _Fundamental structures of computer science_ published in
1983, through Niklaus Wirth in the 1975 _Proceedings of the IEEE Conference on
Reliable Software_ and Clark and Horning in a SIGPLAN paper in September 1973,
to several of Gary Cornell's books on QuickBASIC and Visual BASIC in the
1990s.

This is not a new thing, not language-specific, (clearly!) not addressed by
changing language, nor addressed by types.

~~~
dragonwriter
It actually has a lot to do with types; while it can occur in a few other
situations, the two biggest places it occurs:

(1) Where ints (or some other enumerable value unrelated to the problem
domain) are used in place of self-describing enumerations because of lack of
type system support for enums.

(2) When values within the domain of a type but outside of the domain that
would otherwise be generated are used to signal special situations because of
lack of type system support for sum types.

(The main other place that they occur is with "breakpoints" within a domain of
a normal type, but even these are arguably a workaround for the absence of the
combination of sum types and range-constrained types.)

------
amluto
_sigh_ I lost track of the kernel mitigation for the exploit. I'll go take
care of it (i.e., I just wrote the patch and I'll get it reviewed).

------
phantom_oracle
Sincere question without picking a side:

Is Systemd being coded with the same level of 'care' as OpenSSL was before
being pwned?

~~~
angry_octet
Not at all. OpenSSL was suffering from a lack of effort/funding and a desire
not to 'break' anything. Systemd has plenty of effort and breaks everything,
but a lack of philosophy, a lack of introspection. It's ignoring plenty of
hard lessons about security practice and being very Microsoft-y. Massive
technical debt which we will collectively pay for for a decade or more.

~~~
yuhong
The buggy TLS heartbeat extension was new code I think though.

------
loeg
> systemd was using a magic value (-1) to represent an invalid mode_t value,
> and C's type system did not prevent passing it to the mode argument of open

The open syscall should reject unrecognized flags in the mode argument
(EINVAL), rather than just truncating down to recognized flags. That would
also prevent this specific problem with the sentinel value being used on
accident.

~~~
JdeBP
In the actual open and openat system calls, the mode parameter is a 16-bit
unsigned integer. There are 12 permissions bits and 4 file type bits. There
_are no_ "unrecognized flags". All 16 bits have meaning.

* [http://lxr.free-electrons.com/source/include/uapi/linux/stat...](http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L5)

* [http://lxr.free-electrons.com/source/include/linux/types.h#L...](http://lxr.free-electrons.com/source/include/linux/types.h#L18)

~~~
loeg
Bits above 1<<11 (the non-permission bits) are _not_ valid arguments to
open(2), so I don't see what your point is. In this context, they are invalid.
open(2) should reject values with bits outside of 07777 (== 0x0fff) set,
including (mode_t)-1 (== 0xffff).

Here is the specific place where Linux truncates the bogus mode, instead of
rejecting it: [http://lxr.free-
electrons.com/source/fs/open.c#L906](http://lxr.free-
electrons.com/source/fs/open.c#L906)

(S_IALLUGO defined here: [http://lxr.free-
electrons.com/source/include/linux/stat.h#L9](http://lxr.free-
electrons.com/source/include/linux/stat.h#L9) )

This change would fix this class of issue:

    
    
        --- a/fs/open.c
        +++ b/fs/open.c
        @@ -889,9 +889,11 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
                int lookup_flags = 0;
                int acc_mode = ACC_MODE(flags);
        
        -       if (flags & (O_CREAT | __O_TMPFILE))
        +       if (flags & (O_CREAT | __O_TMPFILE)) {
        +               if ((mode & ~S_IALLUGO) != 0)
        +                       return -EINVAL;
                        op->mode = (mode & S_IALLUGO) | S_IFREG;
        -       else
        +       } else
                        op->mode = 0;
        
                /* Must never be set by userspace */

------
geofft
> _They have even replaced DNS with a dbus-based protocol, which they
> "strongly recommend" applications use instead of DNS._

This seems inaccurate. The phrase "strongly recommend" appears once in the
manpage, where it is strongly recommended that you use _either_ the standard
libc resolver API, with libnss_resolve, or the D-Bus API.

Applications _should_ be using the libc resolver API instead of implementing
DNS themselves. There are some applications like Chrome that implement DNS
themselves because they care very much about DNS; those applications
presumably know how to do all the things systemd-resolved does. Everyone else
should get name resolution functionality from libc. That's what you've been
supposed to do for decades, and it's a standard UNIX interface. That standard
interface supports things like LLMNR that you don't get if you implement DNS
yourself.

Unfortunately, the standard UNIX interface is synchronous, which is why
libraries like ares or adns exist. If you want to use such a library, you can
point it at 127.0.0.53, but you still have the limitations of what can be
expressed in DNS. (And you're still using a nonstandard API to speak to
libares or libadns.) No API exists that is standard, async, _and_ does
everything that libc getaddrinfo() is capable of doing. So systemd built one.

That's pretty standard behavior for systemd: implement compatibility
interfaces where they exist, recommend them if they're good (systemd
explicitly recommends /etc/fstab over writing native mount units, because
/etc/fstab is a perfectly good format), implement them anyway if they're not,
and write a better API, based on D-Bus, when needed. The latter bit not going
through a multi-implementer standards committee isn't great, but it's nowhere
near as bad as presented.

Anyway, this is completely irrelevant to the rest of the analysis, which seems
absolutely correct, and I'm not sure why the author included this parting
shot.

~~~
bigbugbag
Reading the man page it is actually recommending systemd-resolved over other
options.

It says:

\- option 1 (recommended): use systemd-resolved API.

\- option 2: use glibc API with a glibc NSS module to resolve host names via
systemd-resolved.

\- option 3 (not recommended): local DNS stub listener on loopback to connect
direct request to systemd-resolved.

Author included this part to illustrate how the real issue is that systemd is
an unprecedented lock-in. Honestly an init process implementing a DNS
resolver? Where is my kitchen sink ?

~~~
natermer
> Honestly an init process implementing a DNS resolver?

Systemd is a project that manages a large number of low-level services and
programs that work together to try to help create a cohesive operating system.

Systemd is also the name of a init program.

These have the same name, but are not the same thing.

Systemd init process does not provide any DNS resolver features. Systemd-
resolved, however, does.

~~~
bigbugbag
Systemd is an init system, don't take my word for it[1], I seriously doubt
there is any need to add a DNS resolver to an init system, especially one that
reintroduced vulnerabilities.

This "project" you are talking about is this very init system + feature creep
+ mission creep + software bloat + interlocked dependencies to force adoption
+ time.

[1]:
[http://0pointer.de/blog/projects/systemd.html](http://0pointer.de/blog/projects/systemd.html)

~~~
ac29
Your reference link is nearly 7 years old. From the current project homepage
[0]:

"systemd is a suite of basic building blocks for a Linux system. It provides a
system and service manager that runs as PID 1 and starts the rest of the
system. systemd provides aggressive parallelization capabilities, uses socket
and D-Bus activation for starting services, offers on-demand starting of
daemons, keeps track of processes using Linux control groups, maintains mount
and automount points, and implements an elaborate transactional dependency-
based service control logic. systemd supports SysV and LSB init scripts and
works as a replacement for sysvinit. Other parts include a logging daemon,
utilities to control basic system configuration like the hostname, date,
locale, maintain a list of logged-in users and running containers and virtual
machines, system accounts, runtime directories and settings, and daemons to
manage simple network configuration, network time synchronization, log
forwarding, and name resolution."

[0]
[https://freedesktop.org/wiki/Software/systemd/](https://freedesktop.org/wiki/Software/systemd/)

------
grabcocque
Oh great another whine-about-systemd rant.

------
jmclnx
Everytime I see someone mention rust or c++ I stop reading. Might as well
mention COBOL or FORTRAN in the list of 'good' languages, language used is
irrelevant.

Anyway it is a local exploit on an old release, not a good thing but
containable.

~~~
gshulegaard
Apparently relevant reading from the article:

> A language with a better type system, such as Rust or C++ (which has
> std::optional) can help prevent this kind of error.

> That said, this is not about programming languages.

> Rewriting systemd in a safer language would not transform it into quality
> software, ...

