systemd can't handle the process previlege that belongs to user name startswith number, such as 0day #6237

Closed
mapleray opened this Issue Jun 29, 2017 · 37 comments

Comments

Projects
None yet

mapleray commented Jun 29, 2017

Submission type

  • Bug report

systemd version the issue has been seen with

systemd 232

Used distribution

Linux ubuntu 4.10.0-19-generic #21-Ubuntu SMP Thu Apr 6 17:04:57 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

In case of bug report: Expected behaviour you didn't see

The process started by systemd should be user previlege

In case of bug report: Unexpected behaviour you saw

The process started by systemd was root previlege

In case of bug report: Steps to reproduce the problem

linux user's name : 0day
conf file: socat.service

[Unit]
Description=0day socat service
After=network.target

[Service]
User=0day
Restart=always
Type=simple
WorkingDirectory=/home/0day/
ExecStart=/usr/bin/socat TCP-LISTEN:18086,reuseaddr,fork EXEC:"/opt/run-elf"

[Install]
WantedBy=multi-user.target

and then use systemctl start socat.service to start process, but the previlege is root

P.S.
I searched google and found that it was not right to named a linux user with 0day, it should satisfy "^[a-z][-a-z0-9]*\$ , but when I use xinted to start the service, it can handle the previlege rightly with linux user 0day

Owner

poettering commented Jun 29, 2017

Yes, as you found out "0day" is not a valid username. I wonder which tool permitted you to create it in the first place. Note that not permitting numeric first characters is done on purpose: to avoid ambiguities between numeric UID and textual user names.

systemd will validate all configuration data you drop at it, making it hard to generate invalid configuration. Hence, yes, it's a feature that we don't permit invalid user names, and I'd consider it a limitation of xinetd that it doesn't refuse an invalid username.

So, yeah, I don't think there's anything to fix in systemd here. I understand this is annoying, but still: the username is clearly not valid.

I hope that makes sense?

@poettering poettering closed this Jun 29, 2017

Yeah, thanks.

Wouldn't it make sense to refuse User=0day then?

In fact, wouldn't it even make sense to refuse User=0x200 or User=01000 rather than User=512 (in other words, to only allow decimal canonical user IDs) for the same reason?

BTW: where does xinetd come in here?

I wonder which tool permitted you to create it in the first place.

It appears that el7 patches shadow-utils-4.1.5.1/libmisc/chkname.c so that it does allow that kind of usernames, explicitly removing the restrictions: https://gist.github.com/bloerwald/a482791395114fa82636e2ab207cdb11

While it does indeed seem to be a not-a-bug in systemd, one may want to make someone maintaining that el7 patch aware of that the combination of systemd and that patch does pose an issue.

A patch refusing any User= that are invalid would be a nice thing, even if it is not a bug, as noted by @divVerent. That would put the el7 maintainer at the position to explicitly remove that check as well, thinking about their patch allowing usernames without restrictions.

nopil3os commented Jul 1, 2017

I wonder which tool permitted you to create it in the first place.

useradd doesn't complain: screenshot

shevegen commented Jul 1, 2017

Does the POSIX standard deny leading numbers for user names on *nix?

Tested on Debian with shadow-utils 4.4 and adduser 3.115, only adduser complains, useradd doesn't.

# useradd -m 0day
# userdel -r 0day
userdel: 0day mail spool (/var/mail/0day) not found
# adduser 0day
adduser: Please enter a username matching the regular expression configured
via the NAME_REGEX configuration variable.  Use the `--force-badname'
option to relax this check or reconfigure NAME_REGEX.

RealDolos commented Jul 1, 2017

Let's look if it's actually invalid... Probably not because:

3.276 Portable Filename Character Set
The set of characters from which portable filenames are constructed.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 . _ -
The last three characters are the period, underscore, and hyphen characters, respectively.

3.426 User Name
A string that is used to identify a user; see also User Database. To be portable across systems conforming to IEEE Std 1003.1-2001, the value is composed of characters from the portable filename character set. The hyphen should not be used as the first character of a portable user name.

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276

adduser will often refuse it tho, e.g. on debian with NAME_REGEX (calling itself "more conservative by default"), but you can still override that behavior.

So in my humble opinion, it's a valid name and thus systemd has a bug here.

Even if you assume leading numbers are not allowed, systemd running the unit under root is a bug too. It should refuse to run the unit, in particular in the light of:

systemd will validate all configuration data you drop at it
-- @poettering #6237 (comment)

This is clearly not the case, User=0day is clearly not properly validated and silently ignored and it's still a bug (or it's just put into something like atoi and will try to execute 1day as uid 1, not sure about that).

So either way, there is a bug here.

redrifle commented Jul 1, 2017

hm yes I'll look into it

rain-1 commented Jul 1, 2017

I think people are getting distracted with whether or not 0day is a valid name or not.

The real problem here is that the unit is run as root, despite "0day" not describing the root user, isn't it?

Can a systemd developer tell: Will a patch that gives an error in this case be accepted? I can get started on it if so.

@rain-1 there's 2 bugs - one is that it is still running as root and the other is that it doesn't support usernames starting with numbers.

rain-1 commented Jul 1, 2017

I see! please make a separate issue to track the second bug (usernames starting with numbers).

RealDolos commented Jul 1, 2017

Actually, fix the incorrect user name handling thereby fixing the run-as-root issue too. And we kinda made an issue about that already, namely THIS one.

This is a severe security issue that should be fixed, and assigned a CVE, and not not-a-bug.

rain-1 commented Jul 1, 2017

@julian-klode you might have misunderstood this bug, it cannot be used for priv. esc.

@rain-1 If the process starts, and you expect it to start a user and not as root, then that is a security issue, because your service suddenly runs as root (and you might not notice it until it is too late).

pRiVi commented Jul 1, 2017

So many bugs and issues, and all the time the poettering ignorance... I just cannot stand it...

It does not matter if its free, if you carry such things with it. Sadly the only way leaving it is using devuan...

RealDolos commented Jul 1, 2017

Seems the stuff is parsed using:

Meaning indeed, User=1day will try to run with UID 1 as well

Edit: See other comments following up on this probably not being the case after all.

UgnilJoZ commented Jul 1, 2017

@RealDolos My systemd v232 says: Invalid user/group name or numeric ID, ignoring: 7oz and starts the service as root also. Likely because the 7oz as a whole is not a valid number.

# /etc/systemd/system/test.service
[Service]
ExecStart=/bin/id
User=7oz

red-hood commented Jul 1, 2017

If a user name is indeed allowed to be made up of digits alone, there has to be a way to distinguish it from a UID. I propose to specify the information in the same way GNU coreutiles does, by appending a + whenever a UID is meant, and otherwise try to resolve to a username first.

See also: https://www.gnu.org/software/coreutils/manual/coreutils.html#Disambiguating-names-and-IDs

@UgnilJoZ do you actually have a user with uid 7?

UgnilJoZ commented Jul 1, 2017

@RealDolos Yes, I just created one for testing ;)

@RealDolos safe_atou checks that it is a valid number, and not a number with something at the end, so the warning here makes sense. But then the bug report is incomplete, because systemd does at least warn about it. I'm not sure it makes sense to start a service if the specified user is invalid, though.

RealDolos commented Jul 1, 2017

@julian-klode

I'm not sure it makes sense to start a service if the specified user is invalid, though.

That one is easy, in my humble opinion: Same as systemd should not pick and run some random binary when the specified ExecStart is invalid, it shouldn't run a service under a random uid if it cannot find the specified user (uid 0 being the random uid fairly picked by the Debian PRNG, of course)

PS: Sorry, @julian-klode, only saw just now that you're actually a Debian dev. My Debian PRNG quip wasn't meant to personally target you.

red-hood commented Jul 1, 2017

In

if (parse_uid(*username, &u) >= 0) {
, the entry is first parsed as a UID, and only if that fails, a lookup for the username is done. To be compliant, this order has to be changed.

red-hood commented Jul 1, 2017

The line which does the hard-coded fallback to root is here:

if (r < 0)

@red-hood That's not even remotely true - the code is for ConditionUser, and the line mentioned says that if the user could not be parsed (< 0) that the condition is not met (return 0 => false) - and chasing it down step by step in issue comments is not helping anyone.

Now how can this broader issue be fixed? Hard. Very hard. Making a valid User= directive mandatory would be a good start - but this is horribly backwards incompatible

Maybe a systemd config option that turns such warning cases into errors? Or like, a config file that enumerates the kinds of errors that should be fatal instead of warnings?

Contributor

mbiebl commented Jul 2, 2017

Sigh, since this now attracting the trolls, I'm locking the conversation.

@mbiebl mbiebl locked and limited conversation to collaborators Jul 2, 2017

@poettering poettering deleted a comment from jrmithdobbs Jul 2, 2017

@poettering poettering deleted a comment from sergeyfrolov Jul 2, 2017

Owner

poettering commented Jul 2, 2017

Just to summarize things:

  1. systemd is not the one coming up with the restrictions on user names, and while some distributions are less restrictive, many do enforce the same restrictions as we do. In order to make systemd unit files portable between systems we'll hence enforce something that resembles more the universally accepted set, rather than accept the most liberal set possible.
  2. User= in unit files is about system users, not regular users, and I am pretty sure we should enforce a stricter regime about system users than regular users.
  3. In systemd we generally follow the rule that when we encounter a unit setting that does not validate syntax-wise we'll log about it and ignore it, for compat reasons. We do the same for User= here as for all other options. Note that if you specify a valid user name but where the user doesn't exist, then we'll instead fail the service on start, because in that case there's not just something wrong with the syntax the service author used but actually something inconsistent on the system, and that should be considered fatal.
Contributor

martinpitt commented Jul 2, 2017

Agreed about 1 and 2, but I disagree about 3. Setting User=nonexistinguser is syntactically correct, just not semantically. It's perfectly legitimate and consistent to ignore unkonwn keys such as Userr=foo, but we don't have a policy to ignore unknown/invalid values. For example, if a unit has Requires=nonexisting.service, it rightfully fails; we don't just ignore nonexisting.service. For the same reason, a unit with User=nonexistinguser should fail instead of silently running as root. That's the security relevant bit that we ought to fix.

I agree that not handling user names that start with digits can be considered "not a bug".

Owner

poettering commented Jul 2, 2017

For the same reason, a unit with User=nonexistinguser should fail instead of silently running as root.

That's exactly what happens, and what I wrote above: if the username is valid but the user doesn't exist we'll let the unit fail on start. If the username is already invalid syntax-wise we'll log about it but proceed.

Hence, if you write:

User=000fooo...@!

Then we'll ignore the assignment altogether (but log about it), since it's syntactically invalid. But if you specify:

User=waldo

and the user "waldo" does not exist (though it is syntactically valid), then we'll accept the setting, but as soon as you actually try to start the unit it will fail with "user not found".

Contributor

martinpitt commented Jul 2, 2017

Thanks @poettering for the clarification!

Owner

haraldh commented Jul 11, 2017

User @roland found that usernames that contain unicode characters also cause this bug.

The bug also affects usernames that contain unicode characters, which can be created with "useradd" without difficulties:

# useradd -m pöttering
# id pöttering
uid=1002(pöttering) gid=1003(pöttering) Gruppen=1003(pöttering)

# cat /etc/systemd/system/test-unicode.service
[Service]
User = pöttering
ExecStart = /usr/bin/touch /tmp/foo

# ls -l /tmp/foo
ls: cannot access '/tmp/foo': No such file or directory
# systemctl daemon-reload
# systemctl start test-unicode.service
# ls -l /tmp/foo
-rw-r--r-- 1 root root 0 Jul 3 17:56 /tmp/foo

Why it may be unlikely that an admin creates a username starting with a zero and wants to run a service with it I think this eventually might happen with usernames that contains umlauts or acute accents.

Owner

poettering commented Jul 11, 2017

@haraldh we stick to the portable set, that the majority of distros agrees on, and umlauts in user names don't work on fedora, nor arch... I am pretty sure it works on a small minority of distros only. here's the output i get on fedora:

# adduser züpp
adduser: invalid user name 'züpp'

Note that fedora/rhel explicitly patch our the upstream rules of shadow-utils:

https://src.fedoraproject.org/cgit/rpms/shadow-utils.git/tree/shadow-4.1.5.1-goodname.patch#n7

Note that neither the upstream nor the fedora rules actually accept umlauts in usernames.

Anyway, systemd is not a tool for generic user management, it's a tool for service management. We make restrictions on the username you may make use of to keep things safe and portable between distributions, hence we stick to the set of user names that are portable, and complain about all others.

I am happy with changing our rules, but before we do that, please work with the POSIX, shadow-utils, libuser communities, as well with the other Linux distributions to come up with a single unified set of rules, and then we are happy to adopt that too in systemd. But until then, I think it's better for systemd to stick to the rules that only permit names that are portable to all of these systems. After all systemd not only consumes these names, it also potentially creates them (via sysusers.d or when DynamicUser=1 is used), and we should make sure systemd cannot beused as a vehicle for creating users that are otherwise not allowed.

Also: don't forget we don't break people's stuff with this, User=/Group= knew no counterpart in sysvinit, because priv dropping had to be implemented by the daemons manually there. If you do decide to let systemd do the priv dropping for you, we politely ask you to use portable names, so that the unit files work everywhere. And the rules we enforce are neither crazy nor random, they are the common core every distro accepts.

fbuihuu pushed a commit to fbuihuu/systemd that referenced this issue Nov 29, 2017

core/load-fragment: refuse units with errors in certain directives
If an error is encountered in any of the Exec* lines, WorkingDirectory,
SELinuxContext, ApparmorProfile, SmackProcessLabel, Service (in .socket
units), User, or Group, refuse to load the unit. If the config stanza
has support, ignore the failure if '-' is present.

For those configuration directives, even if we started the unit, it's
pretty likely that it'll do something unexpected (like write files
in a wrong place, or with a wrong context, or run with wrong permissions,
etc). It seems better to refuse to start the unit and have the admin
clean up the configuration without giving the service a chance to mess
up stuff.

Note that all "security" options that restrict what the unit can do
(Capabilities, AmbientCapabilities, Restrict*, SystemCallFilter, Limit*,
PrivateDevices, Protect*, etc) are _not_ treated like this. Such options are
only supplementary, and are not always available depending on the architecture
and compilation options, so unit authors have to make sure that the service
runs correctly without them anyway.

Fixes #6237, #6277.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.