2

I have a problem whereby an executable which has the setuid bit set and is owned by root is not having its euid set to 0 sometimes.

I'm running Debian 11 on a small SBC for a control application. I have a systemd service which runs a bash script which runs an executable. The systemd unit file looks like this

[Unit] Description=Runs fdil_run After=network-online.target StartLimitIntervalSec=108000 StartLimitBurst=12 StartLimitAction=reboot [Service] Type=simple User=nxfence ExecStart=/bin/bash /usr/local/bin/fdil_run Restart=always RestartSec=3600 ExecStartPre=/bin/sleep 10 [Install] WantedBy=multi-user.target 

and the bash script looks like this

#!/bin/bash fdil 

As you can see, all the bash script does is run the executable (it's set up like this for historical reasons). The setuid bit is set on the executable -rwsr-xr-x 1 root root. At the start of the executable I have

 ruid = getuid (); euid = geteuid (); 

and a statement to write these values to the system logs so that I can check the values are set correctly.

Most of the time, this set up works fine and checking the logs give ruid = 1000 and euid = 0. Occasionally, however (twice in perhaps the last 30 system boots) the setuid bit is somehow not recognised and inspecting the logs shows ruid = 1000 and euid = 1000. Needless to say, when this happens the code encounters a series of Permission denied errors and hence fails.

Why does it work well most of the time, but fail occasionally? I can only think that it is something to do with systemd needing to reach a particular target before starting the executable in order for the setuid bit to be recognised, but I can find nothing online to suggest this is the case.

One obvious thing would be to get rid of the bash script (since it's not doing anything useful) and run the executable straight from the unit file, but verifying that this has solved the problem would be very difficult because the problem is intermittent. I'd like to know what the underlying cause of the problem is, so that I can know it is solved rather than just trying things at random.

4
  • 3
    Why can't you just call the executable directly from the systemd unit? Commented Oct 1 at 16:59
  • 1
    setuid is managed by the kernel, systemd has nothing to do with it. AFAIK, the only times Linux ignores setuid is if the program is a script (it begins with a #! shebang line) or it's on a filesystem mounted with nosuid. Neither of these would explain the intermittent nature of this problem, unless you're mounting the filesystem with fdil differently. Commented Oct 1 at 19:23
  • 3
    Why not just use User=root in the unit file? Commented Oct 1 at 19:24
  • 2
    Why not configure your script to: If euid != 0 record debug info check /etc/mtab for mounts with the nosuid or users options. (Explore your Mount/Read/Write/Execute problems with https://github.com/waltinator/pathlld, a bash script to show the permissions, mount options along the path to an object or objects.) and exit 1 , and have a wrapper script retry on error. if euid == 0 invoke the program. Commented Oct 1 at 22:33

2 Answers 2

4

Have systemd run the program directly, as the desired user.

Replace with the correct full path to the program. Example changed directives:

User=root ExecStart=/usr/local/bin/fdil 

Defining the user in the unit also limits the privilege escalation of this program to only this service. Usually a good idea if no one else has a need to have root run this.

Why it needs root is an separate question. Most things can be made to work as not-root, file permissions and more.


If you do still intend to troubleshoot setuid, trace while the program is being executed to see what is going on.

setuids.bt from bpftrace can trace these system calls, showing program arguments and return.

If you have Linux auditd available, setuid or execve system calls can be similarly logged. Upstream auditd has some examples of how to log such euid changes, because privilege escalations are interesting events.

1

Several things in Linux can prevent a SUID flag from being applied. Obviously something is different between these boots so my suggestion is to check the situation and compare both cases:

  • mount option nosuid; check /proc/mounts
  • AppArmor & SELinux (configuration could change or it is not always activated); check with apparmor_status / sestatus
  • prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) (NoNewPrivileges= in systemd units); check with systemctl show -p NoNewPrivileges unitname.service

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.