Linux – Safest Way to Programmatically Write to a File with Root Privileges

privilegessetuid

A huge application needs, at one specific time, to perform a small number of writes to a file which requires root permissions. It is not really a file but a hardware interface which is exposed to Linux as a file.

To avoid giving root privileges to the whole application, I wrote a bash script which does the critical tasks. For example, the following script will enable port 17 of the hardware interface as output:

echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction

However, as suid is disabled for bash scripts on my system, I wonder what is the best way to achieve this.

  1. Use some workaround presented here

  2. Call the script with sudo from the main application, and edit the sudoers list accordingly, to avoid requiring a password when calling the script. I'm a little bit uncomfortable to give sudo privileges to echo.

  3. Just write a C program, with fprintf, and set it to suid root. Hardcode the strings and filenames and make sure only root can edit it. Or read the strings from a text file, similarly making sure that no one can edit the file.

  4. Some other solution which didn't occur to me and is safer or simpler then the ones presented above?

Best Answer

You don't need to give sudo access to echo. In fact, that's pointless because, e.g. with sudo echo foo > bar, the redirection is done as the original user, not as root.

Call the small script with sudo, allowing NOPASSWD: access to ONLY that script (and any other similar scripts) by the user(s) who need access to it.

This is always the best/safest way to use sudo. Isolate the small number of commands that need root privileges into their own separate script(s) and allow the un-trusted or partially-trusted user to only run that script as root.

The small sudo-able script(s) should either not take args (or input) from the user (i.e. any other programs it calls should have hard-coded options and args) or it should very careful validate any arguments/input that it has to accept from the user.

Be paranoid in the validation - rather than look for 'known bad' things to exclude, allow only 'known good' things and abort on any mismatch or error or anything even remotely suspicious.

The validation should occur as early in the script as possible (preferably before it does anything else as root).


I really should have mentioned this when I first wrote this answer, but if your script is a shell script it MUST properly quote all variables. Be especially careful to quote variables containing input supplied by the user in any way, but don't assume some variables are safe, QUOTE THEM ALL.

That includes environment variables potentially controlled by the user (e.g. "$PATH", "$HOME", "$USER" etc. And definitely including "$QUERY_STRING" and "HTTP_USER_AGENT" etc in a CGI script). In fact, just quote them all. If you have to construct a command line with multiple arguments, use an array to build the args list and quote that - "${myarray[@]}".

Have I said "quote them all" often enough yet? remember it. do it.