2

My goal is to write something to a file that needs superuser privileges and I want to do it with a single line of code. Steps I have tried:

I can pass a password from stdin to sudo by using its -S option:

printf 'password\n' | sudo -S cat /etc/test.txt

I can also pass a string from stdin to sudo to do something:

echo 'hello' | sudo tee /etc/test.txt

However, I am not able to pass the string and password on the same line. Is this possible? How can I achieve this?

e_moro
  • 119

3 Answers3

3

You wrote

My goal is to write something to a file that needs superuser permission

One way to do this is to relax the permissions on the file so that it has (for example) group write permission. Then add the relevant users to that group. After a logout/login they will then be able to write to the file with no need for sudo.

Another "proper" way to do this with sudo is to create a script that manages the writing process, and then authorise that script within /etc/sudoers so that selected users don't need to provide a password. The big advantage of this is that the target file is not freely accessible even to your target users; your script can validate and sanitise the input before it's written to the file

Get a root shell. Keep this open and do not close it until you are sure that sudo is still working

sudo -s

Now create the script

cat >/usr/local/bin/write-to-file <<'EOF'
#!/bin/bash
#
export PATH=/usr/local/bin:/usr/bin:/bin        # Only necessary for a non-standard PATH

Restart with sudo if we are not already root

[[ $(id -u) != 0 ]] && exec sudo "$0" "$@"

Read just one line into the target file

head -n1 >/etc/test.txt EOF

Still using this root shell, make the script executable and then add a line to sudoers. Change the inital user to the user account that is permitted to run write-to-file without a password, or ALL if any user is allowed.

chown root /usr/local/bin/write-to-file         # Ensure no-one else can modify the script
chmod a+rx,go-w /usr/local/bin/write-to-file    # Make it executable

echo 'user ALL = NOPASSWD: /usr/local/bin/write-to-file' >/etc/sudoers.d/write-to-file

Do not close the root shell yet.

In another (non-root) terminal, test the new script. If you get write-to-file: command not found then /usr/local/bin isn't in your PATH (run export PATH="$PATH:/usr/local/bin")

echo hello | write-to-file                      # Should not prompt for password
cat /etc/test.txt                               # Should contain "hello"

write-to-file < /etc/passwd # Multiline input cat /etc/test.txt # Contains only the first line from passwd

Notice that we didn't even need to use sudo here. What's happened is that the script noticed it wasn't running as root, and restarted itself with sudo.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • I think I understand your idea and I have learned from it. However, it will allow users to operate without entering their passwords. I don't want it at all. I want all users to have to use their passwords every time they want to configurate the target file. For this, my main script should ask for passwords and then pass them to sudo. Or something like that. – e_moro Dec 06 '20 at 14:07
  • 1
    @e_moro remove the NOPASSWD: part of the sudoers entry and stop asking users for their passwords yourself. By default sudo will remember a user's authentication for a short while and won't keep asking for a password each time it's used – Chris Davies Dec 06 '20 at 14:27
  • Anyway, sudo will prompt for the user password, whereas I want my script to pass it. (My program has a simple GUI to introduce it). I'm sorry if my question wasn't clear enough. – e_moro Dec 06 '20 at 19:37
  • Very good answer, informative and very clear. And seems to address the problem as stated, not the method as asked (which is a good thing: focusing on what is the goal, not what was the chosen route to reach it) – Olivier Dulac Sep 07 '23 at 09:26
1

Use a subshell:

(echo "password"; cat /some/file ) | sudo -S tee /etc/test.txt

Where cat /some/file can be any command producing output.

-1

This follows the @roaima's idea of using an auxiliary file to write the target one:

  1. Logged as root, create a simple script called write-to-file, with the follow code:

    echo $1 > /etc/test.txt
    

(it must be placed at /usr/local/bin and must have only root execution permission).

  1. As a normal user, you can run it from anywhere by:

    echo 'password' | sudo -S write-to-file "hello!"
    

In this way, one string is passed as stdin, and the other as an argument.

note: I don't know if this solution can result in a security hole. In my opinion, it should be as safe as the usual use of sudo.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
e_moro
  • 119
  • Since in this scenario there is no limitation on what sudo can do, there's no additional security created or imposed by this solution. It's pretty much identical to sudo -S bash -c 'echo hello, world >/etc/test.txt' or (don't do this) sudo -S bash -c 'echo hello, world >/etc/passwd'. With open and uncontrolled sudo there is no validation to help prevent mistakes – Chris Davies Dec 09 '20 at 00:03