Score:0

Cannot get sudo to work consistently without terminal in non interactive script

ph flag

I have a shell script I wrote that needs sudo access only when needed. It uses Zenity for a GUI and runs without a termninal. 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 wrote 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

uz flag
Jos
You don't use `sudo` inside a script. If the script needs root permissions, run the whole script as root.
Damien Stewart avatar
ph flag
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.
hr flag
@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)
Damien Stewart avatar
ph flag
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.
Score:0
ph flag

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.

muru avatar
us flag
Unless the variable is exported, it's not trivial to peek into a shell script's memory and extract it.
Damien Stewart avatar
ph flag
@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,
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.