2

I'm trying to use sudo with -S to issue the password via command line.

$ sudo -S <<< "mypassword" command

This works, but it doesn't allow for interactive commands. Like nano for example:

$ sudo -S <<< "mypassword" nano /tmp/foo

It automatically closes the nano session. Same with apt-get:

$ sudo -S <<< "mypassword" apt-get install foo

It closes before I can press "y" to install foo. I know I can pass -y with apt but overall, this is frustrating.

Also tried stuff like this, but it still fails to engage with interactive session:

$ sudo -S <<< "mypassword" -H -u root bash -c "apt-get install foo"

Is it possible to use -S with sudo without cancelling all interactive shells?

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
Laura
  • 211
  • 1
    Since with -S you're telling sudo to read from stdin, effectively blocking the child command's access to stdin. Why do you want to use -S and not a NOPASSWD rule? – Olorin Feb 26 '19 at 02:08
  • 2
    Just to point out the obvious, passing passwords in such way is bad idea for security reasons. Passwords can be intercepted and they get stored into shell's history as well. See https://security.stackexchange.com/a/184158/121824 – Sergiy Kolodyazhnyy Feb 26 '19 at 04:55

1 Answers1

6

File descriptors are inherited

What you're using is <<< aka here-string, and that type of redirection is implemented via temporary files that are immediately unlinked (aka deleted). Knowing also that processes inherit file descriptors (and stdin among them)1 from parent to child, when sudo -S is performed, it reads its input from the temporary file that your original shell has passed to it (the one that reads sudo -S command). You can see that in action:

$ sudo -S bash -c 'ls -l /proc/self/fd/0' <<< "mypasswd"
lr-x------ 1 root root 64 Feb 26 13:12 /proc/self/fd/0 -> '/tmp/sh-thd.cUOOXU (deleted)'

Thus, nano reports Too many errors from stdin error in such case. What can be done is to manually re-wire where stdin is coming from.

$ sudo -S bash -c 'exec < /dev/tty; nano /tmp/foobar' <<< "mypasswd"

$ sudo -S bash -c 'nano /tmp/foobar < /dev/tty' <<< "mypasswd"

Either of the two commands will lauch nano properly and other commands as well. /dev/tty is the special file representing current terminal device, and it is effectively the same thing as stdin of "normal" interactive shell (and of course I am oversimplifying that statement).

Another variation on the theme is $ sudo -S bash -c 'nano /tmp/foobar 0>&1' <<<"mypasswd". stdout effectively points to the same terminal device (unless you've explicitly provided a redirection to have stdout point somewhere else). Thus, if we make stdin point to be the duplicate of stdout, it's effectively the same solution as above. (And if you're wondering why I highlighted dup part, that's because dup2() syscall is the one responsible for all the juggling of file descriptors under the hood)


Caution

However, note that I don't encourage using sudo -S in interactive shell, since passwords will remain in shell's history

$ sudo -S bash -c 'sleep 3m' <<< "ohnomypassword"
^C
$ history 2
 2016  sudo -S bash -c 'sleep 3m' <<< "ohnomypassword"
 2017  history 2

sudo -S is more appropriate for use with other processes, where sudo -S would expect input via pipeline, ideally from zenity --password or dialog --passwordbox "foobar text" 200 200 (see extra1 and extra2 about dialog).


1. If file is open with O_CLOEXEC flag set, the child process or replaced process spawned via execve() type of flag won't have access to that file descriptor - that is it will be closed on execve()

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497