This is a multi-part message in MIME format.
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Content-Type: text/x-patch;
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;

--- gnome-ssh-askpass2.c.orig 2007-11-23 19:42:11.000000000 -0700
+++ gnome-ssh-askpass2.c 2007-11-23 19:50:26.000000000 -0700
@@ -35,9 +35,12 @@
* pointer will be grabbed too. These may have some benefit to security if
* you don't trust your X server. We grab the keyboard always.
+char *write_otac_to_fifo(char *);

#define GRAB_TRIES 16
#define GRAB_WAIT 250 /* milliseconds */
+#define OTAC_PWD_LEN 4 /* number of characters in otac passphrase */
+#define OTAC_FIFO_LEN 32 /* max fifo name length */

* Compile with:
@@ -87,11 +90,16 @@
passphrase_dialog(char *message)
const char *failed;
- char *passphrase, *local;
+ char *passphrase, *local, *otac_passphrase, *otac_fifo;
int result, grab_tries, grab_server, grab_pointer;
GtkWidget *dialog, *entry;
GdkGrabStatus status;

+ /* generate and transmit otac passphrase if env var set */
+ otac_fifo=malloc(OTAC_FIFO_LEN);
+ otac_fifo=getenv("SSH_OTAC_FIFO");
+ if (otac_fifo)
+ otac_passphrase=write_otac_to_fifo(otac_fifo);
grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
grab_tries = 0;
@@ -163,14 +171,27 @@
/* Report passphrase if user selected OK */
passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
if (result == GTK_RESPONSE_OK) {
- local = g_locale_from_utf8(passphrase, strlen(passphrase),
- if (local != NULL) {
- puts(local);
- memset(local, '\0', strlen(local));
- g_free(local);
+ if (otac_fifo) {
+ if (strcmp(otac_passphrase,passphrase)==0) {
+ puts("yes");
+ } else {
+ puts("no");
+ }
+ /* Zero otac passphrase in memory */
+ memset(otac_passphrase, '\b', strlen(otac_passphrase));
+ gtk_entry_set_text(GTK_ENTRY(entry), otac_passphrase);
+ memset(otac_passphrase, '\0', strlen(otac_passphrase));
+ g_free(otac_passphrase);
} else {
- puts(passphrase);
+ local = g_locale_from_utf8(passphrase, strlen(passphrase),
+ if (local != NULL) {
+ puts(local);
+ memset(local, '\0', strlen(local));
+ g_free(local);
+ } else {
+ puts(passphrase);
+ }

@@ -198,6 +219,38 @@
return (-1);

+/* generate the one-time agent confirm password and write it to fifo */
+char *
+write_otac_to_fifo(char *otac_fifo)
+ FILE *out;
+ int i,ran,nchars=52,otac_length=OTAC_PWD_LEN;
+ char cpool[52]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX YZ";
+ char *otac_passphrase;
+ /* use random # to select characters for one-time passphrase */
+ /* TODO: substitute better ran-num initializer*/
+ otac_passphrase=malloc(otac_length+1);
+ srandom(time(0));
+ for (i=0;i + ran = random();
+ otac_passphrase[i]=cpool[ran%nchars];
+ }
+ otac_passphrase[otac_length] = 0;
+ /* write otac password to fifo */
+ if ( (out=fopen(otac_fifo,"w")) == NULL) {
+ mkfifo(otac_fifo, 0660);
+ out=fopen(otac_fifo,"w");
+ }
+ fflush(out);
+ fprintf(out,"One-time agent confirm: %s\n",otac_passphrase);
+ fclose(out);
+ /* return otac passphrase */
+ return(otac_passphrase);
main(int argc, char **argv)

Content-Type: text/plain;
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;


This patch modifies gnome-ssh-askpass to optionally generate a one-time password
and transmit it via an out-of-band communication channel. If you can read the
password and enter it back into the gnome-ssh-askpass dialog, ssh-agent is allowed
to continue with the authentication process.

There are two ways to use the modified gnome-ssh-askpass. The first method
incrementally increases the security provided by the ssh-agent/gnome-ssh-askpass
combination. The second allows you to create two fully separated authentication
factors - the private key and one-time password - without using a specialized hardware


ssh-agent executes gnome-ssh-askhsss when an OpenSSH client tries to authenticate
to an SSH server using a private key cached by ssh-agent with the confirm option set.
The gnome-ssh-askpass instance displays a dialog that allows ssh-agent to use the
cached private key when you enter "yes" and/or click the OK button; authentication
can not occur if you don't positively respond to the dialog.

A potential intruder must jump two hurdles to steal your credentials if you use the
ssh-agent private key confirm option. The first is the ability to access a private
key by interacting with your ssh-agent via the socket defined by the SSH_AUTH_SOCK
environmental variable. The second requires access to the your gnome-ssh-askpass
instance. The two hurdles create a simple but effective two-factor authentication

However, gnome-ssh-askpass's two factors are vulnerable to an anyone who gains
user-land access to your workstation. First, both ssh-agent and gnome-ssh-askpass
instances run in a single location -- your workstation. For instance, a bystander
immediately gains access to both factors if you simply walk away from your machine
without setting a screen lock. Second, anyone who breaks into your workstation doesn't
need a root-shell to steal both factors since both ssh-agent and gnome-ssh-askpass
communicate through sockets that you own.


This patch enhances gnome-ssh-askpass's security by giving it an out-of-band
confirmation capability. The modified version optionally generates a secret, called
the one-time confirmation (OTAC) password, and transmits it to a pre-defined
location, typically via e-mail. The valid user must be able to read the secret
passphrase and give it back to gnome-ssh-askpass. If the two match, gnome-ssh-askpass
allows ssh-agent to finish the private key authentication process (and authenticate
you or not depending on whether your private key matches the public key on the
remote machine). Otherwise, you can not authenticate.

How is this different from the original gnome-ssh-askpass? The private key remains
the same as before and is the first factor. You must possess, or have access to,
the private key to start the authentication process. However, you must now obtain
information created by a separate process - gnome-ssh-askpass - through an
out-of-band communication channel to complete the authentication process. The
potential intruder must now have more than access to your workstation to authenticate
to the remote server.


First you cache a private key(s) in ssh-agent using the confirm option. When you
try to authenticate using the cached key, ssh-agent kicks off the patched
gnome-ssh-askpass. If you set SSH_OTAC_FIFO to some non-null value,
gnome-ssh-askpass generates a random string, called the OTAC password and prints it
to the named pipe (FIFO) defined by SSH_OTAC_FIFO. An independent process reads
the OTAC password from the FIFO and transmits it to a pre-defined location.

For instance, run the Perl script in the background (bundled with
this patch). It reads the OTAC password from the FIFO and e-mails it to your pager,
cell-phone or e-mail account. You enter the OTAC password into the
gnome-ssh-askpass dialog once your receive it. The public-key authentication
process is allowed to proceed if the two strings match (but the private key still
has to decrypt the challenge encrypted by the public key); authentication can not
occur if the two don't match.

The following list describes two OTAC-based two-factor authentication scenarios.
The first is the easiest to implement but less secure because it doesn't physically
separate both factors. The second scenario achieves better security, at the cost of
additional complexity, by placing the two factors on separate machines. (Other
scenarios are possible. For instance, the second scenario can be a front-end to a
key repository. However, this hybrid approach requires socket forwarding and is
beyond the scope of this README.)

1. Local confirmation: You cache a private key in a local ssh-agent instance
using the confirm option. When you try to authenticate to the remote machine using
the cached key, ssh-agent sees that the confirm option is set and executes
gnome-ssh-askpass. If the gnome-ssh-askpass instance sees that SSH_OTAC_FIFO exists,
it generates an OTAC password and passes it to via a FIFO defined
by SSH_OTAC_FIFO. The script, running in the background, reads the
OTAC password from the FIFO and e-mails it to you. If you can read the OTAC password,
you enter it in the gnome-ssh-askpass dialog and are authenticated if it matches the

The advantage of the local confirmation scenario is simplicity. You only have to
run on your local workstation, and place your public key in your
authorized_keys file on the remote machine. It's disadvantage is both factors exist
on one machine, your workstation. Local confirmation achieves incrementally greater
security over the un-patched gnome-ssh-askpass by passing the OTAC password to through a FIFO owned by root. The potential intruder must work harder
to gain elevated privileges before stealing the second factor.

2. Remote confirmation: This scenario relies on a two-stage authentication
process, using two private keys. The first stage uses a locally-stored private key
to non-interactively authenticate to the remote machine. That non-interactive
connection kicks off the second stage, which uses a second private key stored on the
remote machine to make an interactive connection to the remote machine. The second
stage starts an ssh-agent instance and caches the second key with its confirmation
option set. This stage continues by making a local and interactive SSH connection to
and from the remote machine, which kicks off gnome-ssh-askpass. This instance of
gnome-ssh-askpass generates and transmits an OTAC password. The second stage run on
the remote machine and is completely separate from your local workstation as is the
OTAC password it generates and transmits.

A little more detail: the first, local private key is tied to a series of shell
commands on the remote machine defined by the "command=" option in your
authorized_keys file. Authenticating to the remote machine executes the following
processes: start an instance of ssh-agent, cache the second private key, with the
confirm option set, and make a second, interactive SSH connection to the loopback
interface on remote machine. When ssh-agent sees the confirm option set, it kicks
off gnome-ssh-askpass. gnome-ssh-askpass generates and transmits an OTAC password
while also opening a dialog that's displayed on your workstation. You authenticate
by entering the correct OTAC password in the dialog.

The advantage of remote confirmation is total, physical separation of both factors.
First, you need access to your private key to start the process. However, once you
make that initial connection, the OTAC password is generated and delivered from the
remote machine. You must possess the designated physical device if you choose to
receive the OTAC password via cell phone or pager; these devices are not, however,
specialized and difficult to obtain like hardware tokens, they're ubiquitous and
cheap. The disadvantage is it requires you to configure each remote machine with
the modified gnome-ssh-askpass and configure the authorized_keys file.


The following examples show how to use both scenarios.


These instructions were written using two Fedora 8 machines, where gnome-ssh-askpass
lives in /usr/libexec/openssh; other distributions like Ubuntu use /usr/lib/openssh.

These instructions assume you've stored the patches and scripts in you home directory
and OpenSSH in /usr/local/openssh-4.7p1.

Both examples, refer to your local workstation, that one you're working from, as
machineA and the remote machine you're connecting/authenticating to as machineB.

You have to be able to execute the configuration instructions as Root or via sudo.

Example 1: local confirmation scenario

1. Log into machineA

2. Set the SSH_OTAC_FIFO environmental variable.
export SSH_OTAC_FIFO=/tmp/ssh-otac.fifo

3. Patch and install the gnome-ssh-askpass2.c program
(assume /usr/local/openssh-4.7p1)
cd /usr/local/openssh-4.7p1/contrib
patch -p0 < ~/gnome-ssh-askpass2.c.patch
make gnome-ssh-askpass2
sudo cp gnome-ssh-askpass2 /usr/libexec/openssh/gnome-ssh-askpass

4. Install and start the script
cp ~/ /usr/local/bin

5. Generate a public/private key-pair
cd ~/.ssh
ssh-keygen -t rsa -f otac-machineB -C otac-B

6. Copy the newly generated public key into you authorized_keys file on machineB
cat | ssh paul@machineB "cat >> ~/.ssh/authorized_keys"

7. Start an ssh-agent instance and cache the newly generated private key using the
confirm option
eval `ssh-agent`
ssh-add -c otac-machineB

8. Connect to the remote machine using using the cached private key.
ssh machineB

Here's what happens at this point on machineA:
A. ssh asks ssh-agent to decrypt the challenge sent from sshd on machineB
B. ssh-agent launches gnome-ssh-askpass because the cached private key has
confirmation set
C. gnome-ssh-askpass generates an OTAC password
D. gnome-ssh-askpass passes the OTAC password to the FIFO designated by the
E. gnome-ssh-askpass opens a dialog on machineA
F. reads the OTAC password from the FIFO
G. e-mails the OTAC password to your e-mail account, pager,
or cell phone

9. You read the OTAC password from the designated channel

10. You enter the OTAC password into the gnome-ssh-askpass dialog

You're authenticated if gnome-ssh-askpass matches the OTAC password you entered
with the original.

11. (OPTIONAL) You can remove password authentication on machineB once you
export SSH_OTAC_FIFO=/tmp/ssh-otac.fifo
sudo /etc/init.d/sshd reload

Example 2: remote confirmation scenario

In this example, the OTAC process occurs on the remote machine, which is also the
one you want to authenticate to.

1. Log into machineA:

2. Generate a public/private key-pair.
cd ~/.ssh
ssh-keygen -t rsa -f otac-machineB -C otac-B
Enter a passphrase when prompted

3. Copy the public key from machineA to your authorized_keys file on
cat | ssh paul@B "cat >> ~/.ssh/authorized_keys"

4. Log into machineB:

5. Modify the ssh server configuration to accept the SSH_OTAC_FIFO variable.
sudo echo "AcceptEnv SSH_OTAC_FIFO" >> /etc/ssh/sshd_config
sudo /etc/init.d/sshd reload

(We need to explicitly send the SSH_OTAC_FIFO variable from machineA to machineB
because the stage 1, of 2, connection does not start an interactive session so we
don't have a chance to define SSH_OTAC_FIFO via the shell.)

6. Install and start the script
cp ~/ /usr/local/bin

7. Patch and install the gnome-ssh-askpass2.c program
(assume /usr/local/openssh-4.7p1).
cd /usr/local/openssh-4.7p1/contrib
patch -p0 < ~/gnome-ssh-askpass2.c.patch
make gnome-ssh-askpass2
sudo cp gnome-ssh-askpass2 /usr/libexec/openssh/gnome-ssh-askpass

8. Generate the second public/private key-pair that will make the second
connection on the loopback interface.
cd ~/.ssh
ssh-keygen -t rsa -f otac-loopback -C otac-loopback
Do not enter a passphrase

9. Copy the newly created public key into the authorized_keys
cat >> authorized_keys

10. Modify the first key's - otac-machineB - entry in authorized_keys so it can
start a second, internal connection using the otac-loopback key. Do this by
pre-pending the "command=" option to the public key entry.

The command/key combination looks similar to the following:

command="eval `ssh-agent`; ssh-add -c ~/.ssh/otac-loopback; ssh -X -o sendenv=SSH_OTAC_FIFO localhost; ssh-agent -k" ssh-rsa AAAAB3NzaC1yc2EAAAABIw ...many many characters... BU9rcQ== otac-remote

The commands designated in the authorized_keys file perform the following
a. eval `ssh-agent` Create an ssh-agent instance
b. ssh-add -c ... Caches the otac-loopback key to ssh-agent using the
confirm option
c. ssh -X -o ... Makes an interactive SSH connection to the loopback
interface from and to remote machineB using the private key cached by
ssh-agent. This connection specifies X forwarding and sends the
SSH_OTAC_FIFO environmental variable to the ssh server.
d. ssh-agent -k Stops the ssh-agent instance when you finish your
session (logout from machineA)

Back on machineA:

11. Set the SSH_OTAC_FIFO environmental variable
export SSH_OTAC_FIFO=/tmp/ssh-otac.fifo

12. Connect to the remote machine using the private key stored on machineA
ssh -X -i ~/.ssh/ssh-machineB -o sendenv=SSH_OTAC_FIFO machineB

What happens on machineB:
A. the ssh connection from machineA is authenticated using the first private
key - ssh-machineB
B. sshd on machineB executes the commands bound to the first public key in
the authenticated_keys file on machineB
C. the commands (from B) launch ssh-agent, caches the second private key
using the confirm option and makes a second ssh connection to the loopback
D. ssh-agent launches gnome-ssh-askpass
E. gnome-ssh-askpass generates the OTAC password
F. gnome-ssh-askpass passes the OTAC password to the FIFO designated by the
SSH_OTAC_FIFO variable
G. gnome-ssh-askpass opens a dialog, which you see on machineA because you're
forwarding X
H. reads the OTAC password
I. e-mails the OTAC password to your e-mail account, pager,
or cell phone

13. Read the OTAC password from whatever channel you use and type it into the
gnome-ssh-askpass dialog.

If the OTAC password you enter into the dialog matches the original, you are

14. (OPTIONAL) You can remove password authentication on machineB once you
export SSH_OTAC_FIFO=/tmp/ssh-otac.fifo
sudo /etc/init.d/sshd reload

In this example, the second factor is generated on the remote machine (B) and sent
through a channel that an intruder who has completely compromised the machine you
are working from (A) can never use as long as you are in possession of the
communication channel such as a cell phone.


This modification allows one to create a true two-factor authentication system. The
factors are the private key, or access to the key, and the OTAC password delivered
via an out-of-band channel. You can create the channel without using a traditional
hardware token by using a simple e-mail account, pager or cell-phone.


Another way to gain two, separate authentication factor would be to add a post
authentication option sshd that generates and transmit a one-time password. sshd_config
could be modified to add an OTAC feature to the AllowUsers option. If set, sshd would
authenticate as normal (password, public-key, etc.), generate an OTAC password and write
it to a FIFO. An external process reads the password and transmits it to the designated
location. The user is authenticated if the user can read and write the password back to
sshd. This system would be simpler to configure than one governed by gnome-ssh-askpass, but
would require modifying the openssh code base.


1. Create a better random number salt.

2. Make the named pipe defined by SSH_OTAC_FIFO writable by the user but readable
by only root.

3. Allow non-graphical interaction.

4. Add logic to generate an OTAC password that a user can also compute. This option
removes the need for the out-of-band communication channel. For instance, the
remote server and user agree on two PINs (one two-digit and one four-digit). The
user asks to authenticate to the remote machine. The remote machine generates the
secret using a formula like: S=date^PIN1 mod(PIN2). The user generates the same
secret using a simple calculator and enters it into gnome-ssh-askpass dialog.
Authentication occurs if the two match (the server generates and compares all
values a minute or two before and after the current time). The intruder must solve
for two unknowns within a short window of a couple of minutes to break this system.

Content-Type: application/x-perl;
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;


# Copyright 2007 Paul Sery
# License: GPL

# Background: if ssh-add caches a private key to ssh-agent
# using the confirm (-c) option, then ssh-agent waits for
# the key to be confirmed before allowing the key to be used
# authenticate an ssh session. ssh-agent calls gnome-ssh-askpass2
# if the environmental variable SSH_OTAC_FIFO is set.
# gnome-ssh-askpass2 displays a dialog and confirms use of the key
# if you click the OK button, or type "yes" and click the OK button.
# This script reads a one-time agent confirm string (password) from a
# fifo and emails it to the designated user. The user enters the string
# in the gnome-ssh-askpass2 dialog. If the string matches the one
# gnome-ssh-askpass2 generated (and wrote to the fifo), then
# gnome-ssh-askpass2 writes "yes" to stdout. ssh-agent reads the "yes"
# and allows the authentication process to continue.

use warnings;
use strict;

my $recipient;
my $debug;

while ($#ARGV >= 0) {
if ($ARGV[0] eq "-d") {
print "Debug on\n";
if ($ARGV[0] ne "-d") {
print "Recipient: $recipient\n";
shift @ARGV;

if (!$recipient) {
print "No reciepient specified\n";

# Don't fork if debug variable set
# (use fork to allow parent to create child process which act as a daemon)
if ($debug) {
} else {
if (fork()) {
exit 0;
} else {

sub main_loop {
my ($debug)=shift;

my $fifo="/tmp/ssh-otac.fifo";
$fifo = $ENV{'SSH_OTAC_FIFO'} if ( exists $ENV{'SSH_OTAC_FIFO'} );
print "[] FIFO: $fifo\n" if $debug;

unless (-p $fifo) { system("mkfifo -m 0666 $fifo"); }
open(OUT,"< $fifo") or die "can't open $fifo: $!";
while (1) {
while ( ) {
print "[] OTAC: $_\n" if $debug;
exit if ($_ eq 'END');
sleep 1;
close OUT;

sub put_otac {
my ($debug)=shift;
my ($recipient)=shift;
my ($otac)=shift;

$otac=(split /:/,$otac)[1];
if ($debug) {
print "[] OTAC: $otac\n";
} else {
my $mail = "/bin/mail -s 'OTAC: $otac' $recipient";
open(MAIL, "|$mail") or die "Cannot open $mail: $!";
print MAIL "\t\t$otac\n";
close MAIL;

Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

openssh-unix-dev mailing list