0

I have a shell script I wrote that needs sudo access only when needed. It uses Zenity for a GUI and runs without a terminal. The script was working fine but I had one issue with it. It uses Zenity to ask for user password and stores it in a variable. I didn't like how the password was stored as plain text in a variable so wanted to rewrite it to be more secure. Note that the password isn't stored inside the script, just inside a variable on demand.

I then considered to encrypt the password with a randomly generated key using OpenSSL which I tested as working. The only problem I had with that idea is there is a passcode with a passkey so anyone just needs to look at my script source and see how to decode it. Extract the variable contents and reveal it. So a bit like installing a keycode on your door and obfuscating the unlock code in the meter box. Just a deterrent but the info is still there. Perhaps I'm just being a bit over pedantic. :-)

I found I could could pipe the password directly from Zenity to sudo and verify it with -vS options. Great. There's still a pipe in between with plain text but at least it isn't stored in the process. I had that working fine. So changed my script to set a password flag. If the flag is set the credentials are confirmed.

I tested it as all working from a terminal. So then needed to test it running normally without the terminal. But it started failing.

My problems started when I wanted to validate the credentials. Whenever it needs another root operation it validates the credentials again just in case they timed out. But I found they failed. My script kept asking for a password again. This is seconds after logging in with "-vS" and already verifying it. It didn't make sense.

I then found that all other sudo operations kept failing. And all other file operations I needed to do returned fail from sudo as well. It seemed I was stuck with password plain text again. At this point using -S option works fine if I give it a password every time to run a command.

The following is my function that checks and verifies the password credentials:

get_password()
{
    if [ $password -eq 1 ]; then
        sudo -vn
        if [ $? -ne 0 ]; then
            password=0
        fi
    fi

    if [ $password -eq 0 ]; then
        zenity --title="$wt" --width=$ww --password | sudo -vS
        if [ $? -ne 0 ]; then
            zenity \
            --title="$wt" \
            --width=$qw \
            --error \
            --text="Error:\n\nThe password entered is incorrect."

            return 1
        else
            password=1
        fi
    fi

    return 0
}

The following are some file routines called once it has root access:

remove_file()
{
   sudo rm "$1"
}

remove_folder()
{
   sudo rm -R "$1"
}

When I found the problem I tested a few different versions of sudo I had on different Linux installs. All acted differently. I did a search for sudo bugs and didn't find anything specific to the behaviour I experienced. It would be good if there was some consistent behaviour I could rely on. But here are differences I found in return codes:

  • 1.8.3p1-1: sudo -vS:0, sudo -vn:1, sudo cmd:1
  • 1.8.9p5-1: sudo -vS:0, sudo -vn:0, sudo cmd:1
  • 1.8.21p2-3: sudo -vS:0, sudo -vn:0, sudo cmd:0
  • 1.9.13p3-3: sudo -vS:0, sudo -vn:0, sudo cmd:1

As you can see there is no consistency! The earliest only passes on verifying password through stdin and fails all else. The rest all pass on validation. But only one can run a command as root and it is not the newest one. On top of this I found on the newest it can work with sudo using rm for removing a file but not a folder. This is too confusing!

So is there any way around this? Are there any options I should be passing? I've read the manual and looked up guides. I've seen nothing to exactly describe this. I've tried using -n option to no avail. I've even tried sending a fake password with -S but it didn't like it. I'm somewhat stumped by this or I wouldn't be asking. I really don't want to go back to plain text passwords.

Pertaining to the sudoers config, this would be default. I have no need nor desire to modify this. This is also needs to work on other peoples systems so I consider it inappropriate to need any modification that way. The script is packaged as deb. So should be able to work as standard once installed.

I actually would have written a tutorial on using sudo in a background script without storing plain text passwords, with the research I've done, but since I cannot get it consistently work I won't be doing that yet. :-D

4
  • You don't use sudo inside a script. If the script needs root permissions, run the whole script as root.
    – Jos
    Jul 11, 2023 at 11:23
  • Using sudo inside a script is very common. I've avoided running the whole script as root. Because I don't want all files files created or modified to be owned by root. Jul 11, 2023 at 13:05
  • @DamienStewart for that use case, you may find it simple to run the script as root and drop privileges where required (with sudo -u "$SUDO_USER" or similar) Jul 11, 2023 at 15:05
  • I've considered how that would be done. I would need to "invert" my code so all root file operations run as normal. And all user operations would need wrapping to change user. Jul 12, 2023 at 4:23

1 Answer 1

0

After consideration and testing I can see no way for this to work transparently across versions. Not without storing credentials in some form. Which cannot be avoided unless a specific working version of sudo is depended on.

Options for credentials can be stored in variables which is visible, hidden in files which can be found or stored in the kernel keychain using keyctl. The latter looks to be the safest as sensitive data can be protected on a per process level. Though it would still in be clear form if it matters. So can be encrypted as well for extra security.

The askpass option can then be used to retrieve credentials when needed. This will allow the most transparent operation. An external script can be used to pipe the credentials. The credentials can then be verified initially. From then on sudo can be used normally and it if wants credentials again it can use askpass on demand, while the script can issue a simple sudo command that's compatible.

2
  • 1
    Unless the variable is exported, it's not trivial to peek into a shell script's memory and extract it.
    – muru
    Aug 4, 2023 at 15:24
  • @muru Generally the issue is passing it as an argument as it can then be sniffed out from the command stream. Or otherwise the shell variables can be looked with a command like ps. Usually in conjunction with gdb for advanced sniffers, Aug 4, 2023 at 16:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .