reading SSH password from stdin – the openssh 5.6p1 compatible way
Linux Dezember 6th. 2010, 10:05pmThere are several solutions for piping a password to ssh. One of the solutions can be found under http://www.debian-administration.org/articles/587. But starting from openssh 5.6p1 the approach described there won’t work anymore. The reason behind is the following piece of code, that was added in version 5.6:
/* * Discard other fds that are hanging around. * These can cause problem with backgrounded * ssh processes started by ControlPersist. */ closefrom(STDERR_FILENO + 1);
closefrom(int startingfd); closes all opened file descriptors starting from startingfd. In this case it closes all file descriptors starting from stderr (stderr is equivalent to two). At this point every approach that passes the password by inheriting file descriptors to ssh will fail. That means another solution must be found, mine is the usage of SSH_ASKPASS.
The SSH_ASKPASS approach
As stated in the ssh man page, the environment variable SSH_ASKPASS can be used to pass a password to ssh:
If ssh needs a passphrase, it will read the passphrase from the current terminal if it was run from a terminal. If ssh does not have a terminal associated with it but DISPLAY and SSH_ASKPASS are set, it will execute the program specified by SSH_ASKPASS and open an X11 window to read the passphrase. This is particularly useful when calling ssh from a .xsession or related script. (Note that on some machines it may be necessary to redirect the input from /dev/null to make this work.)
To retrieve the password, ssh calls the program specified in the SSH_ASKPASS environment variable. In a perfect world it would be sufficient to set the DISPLAY variable to a bogus value and tell ssh to use the program in SSH_ASKPASS to retrieve the password. But this is not enough, because ssh always looks for a valid terminal and always asks there for a password. The trick is to detach ssh from the current terminal by starting it in a new session. The command setsid – a wrapper around the setsid system call – does exactly this.
sshaskpass script
I put the pieces together and wrote a script that passes a password from stdin to ssh. First the script creates a temp file by callin mktemp and sets then the file permissions so that only the user can read/write the file. Then it writes the password to the temp file where it is read later via the SSH_ASKPASS mechanism. The temp file containing the password is delete at the end of the script or in case the script receives a signal, the file is delete by a signal handler.
#!/bin/bash
#
# script that passes password from stdin to ssh.
#
# Copyright (C) 2010 André Frimberger <andre OBVIOUS_SIGN frimberger.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
if [ -n "$SSH_ASKPASS_TMPFILE" ]; then
cat “$SSH_ASKPASS_TMPFILE”
exit 0
elif [ $# -lt 1 ]; then
echo “Usage: echo password | $0 <ssh command line options>” >&2
exit 1
fi
sighandler() {
rm “$TMP_PWD”
}
TMP_PWD=$(mktemp)
chmod 600 “$TMP_PWD”
trap ’sighandler’ SIGHUP SIGINT SIGQUIT SIGABRT SIGKILL SIGALRM SIGTERM
export SSH_ASKPASS=$0
export SSH_ASKPASS_TMPFILE=$TMP_PWD
[ "$DISPLAY" ] || export DISPLAY=dummydisplay:0
read password
echo $password >> “$TMP_PWD”
# use setsid to detach from tty
exec setsid “$@”
rm “$TMP_PWD”
Q & A
Q: How to use the script
echo “my_password” | ./sshaskpass.sh ssh user@someMachine “ls -l”
Q: Why don’t you use public keys instead?
Public key authentication doesn’t work with pam_mount. This is because pam_mount passes the ssh password to mount.fuse.
April 22nd, 2012 at 16:26
I have been waiting for this answer for so many months. I will test this script in my production site and comment. Thank you very much.
August 7th, 2012 at 18:42
Thx for this awesome trick! I was searching such solution for months!
Januar 20th, 2013 at 10:30
Hello, I have modified your code so that password does not have to be written to a temporary file:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if [ -n "$SSH_ASKPASS_PASSWORD" ]; then
cat <<< "$SSH_ASKPASS_PASSWORD"
elif [ $# -lt 1 ]; then
echo "Usage: echo password | $0 <ssh command line options>" >&2
exit 1
else
read SSH_ASKPASS_PASSWORD
export SSH_ASKPASS=$0
export SSH_ASKPASS_PASSWORD
[ "$DISPLAY" ] || export DISPLAY=dummydisplay:0
# use setsid to detach from tty
strace "$@" </dev/null
fi
März 7th, 2013 at 17:01
is there any way to do this without having setsid? i’m on z/os uss and there is no setsid as a shell command / executable. neither public keys nor expect is an option for me so i’ve to stick with the askpass aproach. thanks in advance! damo
März 7th, 2013 at 17:44
I don’t know any other solution than “setsid”. The good news is, that the setsid executable is not more than a small wrapper around the setsid system call (see setsid.c in util-linux). According to http://publib.boulder.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxb600%2Fssi.htm the setsid system call is available for z/OS, too. Therefore I suggest to use setsid from util-linux.
Juli 15th, 2013 at 10:45
Creating a pty with the script command will (internally) make use of the setsid command as well.
März 3rd, 2014 at 16:31
André, that’s great solution. Thanks a lot 🙂
Februar 17th, 2016 at 13:23
Nice idea, but using a temporary file is somehow dangerous, since any process with sufficient permissions might read it (OK in your case it’s you, but there are cases were SSH keys are generated by a webserver process and used to encrypt/decrypt files there). So an alternative is to write the password to an environment variable and read it from there.
März 26th, 2016 at 18:04
Well, this is only the half truth. A user with sufficient permissions can also read environment variables of arbitrary processes:
2
3
4
5
6
7
8
9
10
11
12
DESKTOP_SESSION=/usr/share/xsessions/plasma
USER=andre
BROWSER=/usr/bin/xdg-open
GTK2_RC_FILES=/etc/gtk-2.0/gtkrc:/home/andre/.gtkrc-2.0:/home/andre/.config/gtkrc-2.0
LC_MEASUREMENT=de_DE.UTF-8
XDG_SESSION_TYPE=x11
DISPLAY=:0
HOME=/home/andre
LC_PAPER=de_DE.UTF-8
PWD=/home/andre
[...]
2
-r-------- 1 andre users 0 26. Mär 17:59 /proc/4085/environ
As you can see, storing a password in an environment variable is either more or less secure than storing it in a permission restricted file.
April 6th, 2016 at 14:41
Hi, thank a lot. that’s so much help. I can use that for scp too, but the problem is upload progress not shown. How to make that scp progress shown on scp command? thanks again