Enclosed is a patch against 2.2.0p1 that teaches ssh (and therefore slogin and scp) how to invoke ssh-askpass to request a password, RSA/DSA key passphrase, or an skey challenge response. I've tested this on Linux (i386), for passwords and RSA/DSA key passphrases. I cannot easily test whether the Right Thing will happen for skey challenge responses; I would appreciate it if someone who uses skey would apply this patch and see if it works properly. In the process of making this patch, I fixed a bug in the ssh_askpass() function (it assumed the string read from the pipe to ssh-askpass would always contain a trailing newline, which is a false assumption). Also, the ssh2_try_passwd() function appears to be broken, in that it contains partial logic to prompt for the correct password multiple times, but the flow of execution through the function guarantees that it can never ask for the password more than once. I wasn't sure what was intended here, so my patched version of ssh2_try_passwd() keeps the same (broken?) logic. Regards, -- James Ralston, Information Technology Software Engineering Institute Carnegie Mellon University, Pittsburgh, PA, USA -------------- next part -------------- diff -U 3 -N -r ORIG/openssh-2.2.0p1/readpass.c openssh-2.2.0p1/readpass.c --- ORIG/openssh-2.2.0p1/readpass.c Thu Jun 22 07:32:32 2000 +++ openssh-2.2.0p1/readpass.c Sat Sep 9 01:10:07 2000 @@ -117,3 +117,48 @@ memset(buf, 0, sizeof(buf)); return (p); } + +/* + * Reads a passphrase by calling ssh-askpass. Returns the passphrase + * (allocated with xmalloc), being very careful to ensure that no + * other userland buffer is storing the password. + */ +char * +ssh_askpass(char *askpass, char *msg) +{ + pid_t pid; + size_t len; + char *nl, *pass; + int p[2], status; + char buf[1024]; + + if (askpass == NULL) + fatal("internal error: askpass undefined"); + if (pipe(p) < 0) + fatal("ssh_askpass: pipe: %s", strerror(errno)); + if ((pid = fork()) < 0) + fatal("ssh_askpass: fork: %s", strerror(errno)); + if (pid == 0) { + close(p[0]); + if (dup2(p[1], STDOUT_FILENO) < 0) + fatal("ssh_askpass: dup2: %s", strerror(errno)); + execlp(askpass, askpass, msg, (char *) 0); + fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); + } + close(p[1]); + len = read(p[0], buf, sizeof buf); + close(p[0]); + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + break; + if (len <= 1) + return xstrdup(""); + if (! (len == sizeof buf)) + buf[len] = '\0'; + nl = strchr(buf, '\n'); + if (nl) + *nl = '\0'; + pass = xstrdup(buf); + memset(buf, 0, sizeof(buf)); + return pass; +} diff -U 3 -N -r ORIG/openssh-2.2.0p1/ssh-add.c openssh-2.2.0p1/ssh-add.c --- ORIG/openssh-2.2.0p1/ssh-add.c Mon Aug 28 20:33:51 2000 +++ openssh-2.2.0p1/ssh-add.c Sat Sep 9 01:10:07 2000 @@ -65,44 +65,6 @@ fprintf(stderr, "Failed to remove all identitities.\n"); } -char * -ssh_askpass(char *askpass, char *msg) -{ - pid_t pid; - size_t len; - char *nl, *pass; - int p[2], status; - char buf[1024]; - - if (askpass == NULL) - fatal("internal error: askpass undefined"); - if (pipe(p) < 0) - fatal("ssh_askpass: pipe: %s", strerror(errno)); - if ((pid = fork()) < 0) - fatal("ssh_askpass: fork: %s", strerror(errno)); - if (pid == 0) { - close(p[0]); - if (dup2(p[1], STDOUT_FILENO) < 0) - fatal("ssh_askpass: dup2: %s", strerror(errno)); - execlp(askpass, askpass, msg, (char *) 0); - fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); - } - close(p[1]); - len = read(p[0], buf, sizeof buf); - close(p[0]); - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - break; - if (len <= 1) - return xstrdup(""); - nl = strchr(buf, '\n'); - if (nl) - *nl = '\0'; - pass = xstrdup(buf); - memset(buf, 0, sizeof(buf)); - return pass; -} - void add_file(AuthenticationConnection *ac, const char *filename) { diff -U 3 -N -r ORIG/openssh-2.2.0p1/ssh.h openssh-2.2.0p1/ssh.h --- ORIG/openssh-2.2.0p1/ssh.h Tue Aug 22 20:46:25 2000 +++ openssh-2.2.0p1/ssh.h Sat Sep 9 01:10:07 2000 @@ -426,6 +426,12 @@ */ char *read_passphrase(const char *prompt, int from_stdin); +/* + * Reads a passphrase by calling ssh-askpass. Returns the passphrase + * (allocated with xmalloc), being very careful to ensure that no + * other userland buffer is storing the password. + */ +char *ssh_askpass(char *askpass, char *msg); /*------------ Definitions for logging. -----------------------*/ diff -U 3 -N -r ORIG/openssh-2.2.0p1/sshconnect1.c openssh-2.2.0p1/sshconnect1.c --- ORIG/openssh-2.2.0p1/sshconnect1.c Tue Aug 22 20:46:25 2000 +++ openssh-2.2.0p1/sshconnect1.c Sat Sep 9 01:13:35 2000 @@ -191,6 +191,8 @@ char *passphrase, *comment; int type, i; int plen, clen; + int interactive = isatty(STDIN_FILENO); + char *askpass = NULL; /* Try to load identification for the authentication key. */ public = key_new(KEY_RSA); @@ -244,7 +246,15 @@ snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", comment); if (!options.batch_mode) - passphrase = read_passphrase(buf, 0); + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = SSH_ASKPASS_DEFAULT; + passphrase = ssh_askpass(askpass, buf); + } else { + passphrase = read_passphrase(buf, 0); + } else { debug("Will not query passphrase for %.100s in batch mode.", comment); @@ -602,6 +612,9 @@ int payload_len; unsigned int clen; char *challenge, *response; + int interactive = isatty(STDIN_FILENO); + char *askpass = NULL; + char buf[300]; debug("Doing skey authentication."); @@ -625,13 +638,30 @@ if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! " "Reponse will be transmitted in clear text."); - fprintf(stderr, "%s\n", challenge); + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = SSH_ASKPASS_DEFAULT; + snprintf(buf, sizeof buf, + "Challenge: \"%s\"; enter response:", challenge); + } else { + fprintf(stderr, "%s\n", challenge); + } xfree(challenge); fflush(stderr); for (i = 0; i < options.number_of_password_prompts; i++) { - if (i != 0) - error("Permission denied, please try again."); - response = read_passphrase("Response: ", 0); + if (!interactive && getenv("DISPLAY")) { + if (i != 0) + response = ssh_askpass(askpass, + "Permission denied, please try again:"); + else + response = ssh_askpass(askpass, buf); + } else { + if (i != 0) + error("Permission denied, please try again."); + response = read_passphrase("Response: ", 0); + } packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); packet_put_string(response, strlen(response)); memset(response, 0, strlen(response)); @@ -657,14 +687,31 @@ { int type, i, payload_len; char *password; + int interactive = isatty(STDIN_FILENO); + char *askpass = NULL; debug("Doing password authentication."); if (options.cipher == SSH_CIPHER_NONE) log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) { + askpass = getenv(SSH_ASKPASS_ENV); + } else { + askpass = SSH_ASKPASS_DEFAULT; + } + } for (i = 0; i < options.number_of_password_prompts; i++) { - if (i != 0) - error("Permission denied, please try again."); - password = read_passphrase(prompt, 0); + if (!interactive && getenv("DISPLAY")) { + if (i != 0) + password = ssh_askpass(askpass, + "Permission denied, please try again:"); + else + password = ssh_askpass(askpass, prompt); + } else { + if (i != 0) + error("Permission denied, please try again."); + password = read_passphrase(prompt, 0); + } packet_start(SSH_CMSG_AUTH_PASSWORD); packet_put_string(password, strlen(password)); memset(password, 0, strlen(password)); diff -U 3 -N -r ORIG/openssh-2.2.0p1/sshconnect2.c openssh-2.2.0p1/sshconnect2.c --- ORIG/openssh-2.2.0p1/sshconnect2.c Tue Aug 22 20:46:25 2000 +++ openssh-2.2.0p1/sshconnect2.c Sat Sep 9 01:10:30 2000 @@ -264,16 +264,30 @@ static int attempt = 0; char prompt[80]; char *password; + int interactive = isatty(STDIN_FILENO); + char *askpass = NULL; if (attempt++ >= options.number_of_password_prompts) return 0; - - if(attempt != 1) - error("Permission denied, please try again."); - + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = SSH_ASKPASS_DEFAULT; + } snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", server_user, host); - password = read_passphrase(prompt, 0); + if (!interactive && getenv("DISPLAY")) { + if (attempt != 1) + password = ssh_askpass(askpass, + "Permission denied, please try again:"); + else + password = ssh_askpass(askpass, prompt); + } else { + if (attempt != 1) + error("Permission denied, please try again."); + password = read_passphrase(prompt, 0); + } packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(server_user); packet_put_cstring(service); @@ -374,6 +388,8 @@ Key *k; int ret = 0; struct stat st; + int interactive = isatty(STDIN_FILENO); + char *askpass = NULL; if (stat(filename, &st) != 0) { debug("key does not exist: %s", filename); @@ -389,7 +405,15 @@ snprintf(prompt, sizeof prompt, "Enter passphrase for DSA key '%.100s': ", filename); - passphrase = read_passphrase(prompt, 0); + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = SSH_ASKPASS_DEFAULT; + passphrase = ssh_askpass(askpass, prompt); + } else { + passphrase = read_passphrase(prompt, 0); + } success = load_private_key(filename, passphrase, k, NULL); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase);