- markus@cvs.openbsd.org 2003/01/23 13:50:27
     [authfd.c authfd.h readpass.c ssh-add.1 ssh-add.c ssh-agent.c]
     ssh-add -c, prompt user for confirmation (using ssh-askpass) when
     private agent key is used; with djm@; test by dugsong@, djm@;
     ok deraadt@
diff --git a/ChangeLog b/ChangeLog
index 7ecd2f7..2fb0b94 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,11 @@
    - jmc@cvs.openbsd.org 2003/01/23 08:58:47
      [sshd_config.5]
      typos; ok millert@
+   - markus@cvs.openbsd.org 2003/01/23 13:50:27
+     [authfd.c authfd.h readpass.c ssh-add.1 ssh-add.c ssh-agent.c]
+     ssh-add -c, prompt user for confirmation (using ssh-askpass) when
+     private agent key is used; with djm@; test by dugsong@, djm@; 
+     ok deraadt@
 
 20030123
  - (djm) OpenBSD CVS Sync
@@ -1062,4 +1067,4 @@
      save auth method before monitor_reset_key_state(); bugzilla bug #284;
      ok provos@
 
-$Id: ChangeLog,v 1.2583 2003/01/24 00:34:52 djm Exp $
+$Id: ChangeLog,v 1.2584 2003/01/24 00:36:23 djm Exp $
diff --git a/authfd.c b/authfd.c
index f04e085..a186e01 100644
--- a/authfd.c
+++ b/authfd.c
@@ -35,7 +35,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: authfd.c,v 1.57 2002/09/11 18:27:26 stevesk Exp $");
+RCSID("$OpenBSD: authfd.c,v 1.58 2003/01/23 13:50:27 markus Exp $");
 
 #include <openssl/evp.h>
 
@@ -499,10 +499,10 @@
 
 int
 ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key,
-    const char *comment, u_int life)
+    const char *comment, u_int life, u_int confirm)
 {
 	Buffer msg;
-	int type, constrained = (life != 0);
+	int type, constrained = (life || confirm);
 
 	buffer_init(&msg);
 
@@ -532,6 +532,8 @@
 			buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME);
 			buffer_put_int(&msg, life);
 		}
+		if (confirm != 0)
+			buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM);
 	}
 	if (ssh_request_reply(auth, &msg, &msg) == 0) {
 		buffer_free(&msg);
@@ -545,7 +547,7 @@
 int
 ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment)
 {
-	return ssh_add_identity_constrained(auth, key, comment, 0);
+	return ssh_add_identity_constrained(auth, key, comment, 0, 0);
 }
 
 /*
diff --git a/authfd.h b/authfd.h
index 38ee49e..2a8751e 100644
--- a/authfd.h
+++ b/authfd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: authfd.h,v 1.31 2002/09/11 18:27:25 stevesk Exp $	*/
+/*	$OpenBSD: authfd.h,v 1.32 2003/01/23 13:50:27 markus Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -51,6 +51,7 @@
 #define SSH2_AGENTC_ADD_ID_CONSTRAINED		25
 
 #define	SSH_AGENT_CONSTRAIN_LIFETIME		1
+#define	SSH_AGENT_CONSTRAIN_CONFIRM		2
 
 /* extended failure messages */
 #define SSH2_AGENT_FAILURE			30
@@ -76,7 +77,8 @@
 Key	*ssh_get_first_identity(AuthenticationConnection *, char **, int);
 Key	*ssh_get_next_identity(AuthenticationConnection *, char **, int);
 int	 ssh_add_identity(AuthenticationConnection *, Key *, const char *);
-int	 ssh_add_identity_constrained(AuthenticationConnection *, Key *, const char *, u_int);
+int	 ssh_add_identity_constrained(AuthenticationConnection *, Key *,
+    const char *, u_int, u_int);
 int	 ssh_remove_identity(AuthenticationConnection *, Key *);
 int	 ssh_remove_all_identities(AuthenticationConnection *, int);
 int	 ssh_lock_agent(AuthenticationConnection *, int, const char *);
diff --git a/readpass.c b/readpass.c
index 96b7e84..95ec5d8 100644
--- a/readpass.c
+++ b/readpass.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: readpass.c,v 1.27 2002/03/26 15:58:46 markus Exp $");
+RCSID("$OpenBSD: readpass.c,v 1.28 2003/01/23 13:50:27 markus Exp $");
 
 #include "xmalloc.h"
 #include "readpass.h"
@@ -46,11 +46,11 @@
 		fatal("internal error: askpass undefined");
 	if (pipe(p) < 0) {
 		error("ssh_askpass: pipe: %s", strerror(errno));
-		return xstrdup("");
+		return NULL;
 	}
 	if ((pid = fork()) < 0) {
 		error("ssh_askpass: fork: %s", strerror(errno));
-		return xstrdup("");
+		return NULL;
 	}
 	if (pid == 0) {
 		seteuid(getuid());
@@ -79,6 +79,11 @@
 		if (errno != EINTR)
 			break;
 
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		memset(buf, 0, sizeof(buf));
+		return NULL;
+	}
+
 	buf[strcspn(buf, "\r\n")] = '\0';
 	pass = xstrdup(buf);
 	memset(buf, 0, sizeof(buf));
@@ -115,7 +120,10 @@
 			askpass = getenv(SSH_ASKPASS_ENV);
 		else
 			askpass = _PATH_SSH_ASKPASS_DEFAULT;
-		return ssh_askpass(askpass, prompt);
+		if ((ret = ssh_askpass(askpass, prompt)) == NULL)
+			if (!(flags & RP_ALLOW_EOF))
+				return xstrdup("");
+		return ret;
 	}
 
 	if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
diff --git a/ssh-add.1 b/ssh-add.1
index 2a34a51..66a8f97 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-add.1,v 1.35 2002/06/19 00:27:55 deraadt Exp $
+.\"	$OpenBSD: ssh-add.1,v 1.36 2003/01/23 13:50:27 markus Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -45,7 +45,7 @@
 .Nd adds RSA or DSA identities to the authentication agent
 .Sh SYNOPSIS
 .Nm ssh-add
-.Op Fl lLdDxX
+.Op Fl lLdDxXc
 .Op Fl t Ar life
 .Op Ar
 .Nm ssh-add
@@ -93,6 +93,14 @@
 The lifetime may be specified in seconds or in a time format
 specified in
 .Xr sshd 8 .
+.It Fl c
+Indicates that added identities should be subject to confirmation before
+being used for authentication. Confirmation is performed by the 
+.Ev SSH_ASKPASS
+program mentioned below. Successful confirmation is signaled by a zero
+exit status from the 
+.Ev SSH_ASKPASS
+program, rather than text entered into the requester.
 .It Fl s Ar reader
 Add key in smartcard
 .Ar reader .
diff --git a/ssh-add.c b/ssh-add.c
index 4f4ab3a..0c2ce16 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -35,7 +35,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh-add.c,v 1.64 2002/11/21 23:03:51 deraadt Exp $");
+RCSID("$OpenBSD: ssh-add.c,v 1.65 2003/01/23 13:50:27 markus Exp $");
 
 #include <openssl/evp.h>
 
@@ -70,6 +70,9 @@
 /* Default lifetime (0 == forever) */
 static int lifetime = 0;
 
+/* User has to confirm key use */
+static int confirm = 0;
+
 /* we keep a cache of one passphrases */
 static char *pass = NULL;
 static void
@@ -165,12 +168,16 @@
 		}
 	}
 
-	if (ssh_add_identity_constrained(ac, private, comment, lifetime)) {
+ 	if (ssh_add_identity_constrained(ac, private, comment, lifetime,
+ 	    confirm)) {
 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
 		ret = 0;
 		if (lifetime != 0)
 			fprintf(stderr,
 			    "Lifetime set to %d seconds\n", lifetime);
+ 		if (confirm != 0)
+			fprintf(stderr,
+			    "The user has to confirm each use of the key\n");
 	} else if (ssh_add_identity(ac, private, comment)) {
 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
 		ret = 0;
@@ -292,6 +299,7 @@
 	fprintf(stderr, "  -x          Lock agent.\n");
 	fprintf(stderr, "  -X          Unlock agent.\n");
 	fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
+	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
 #ifdef SMARTCARD
 	fprintf(stderr, "  -s reader   Add key in smartcard reader.\n");
 	fprintf(stderr, "  -e reader   Remove key in smartcard reader.\n");
@@ -319,7 +327,7 @@
 		fprintf(stderr, "Could not open a connection to your authentication agent.\n");
 		exit(2);
 	}
-	while ((ch = getopt(argc, argv, "lLdDxXe:s:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:")) != -1) {
 		switch (ch) {
 		case 'l':
 		case 'L':
@@ -333,6 +341,9 @@
 				ret = 1;
 			goto done;
 			break;
+		case 'c':
+			confirm = 1;
+			break;
 		case 'd':
 			deleting = 1;
 			break;
diff --git a/ssh-agent.c b/ssh-agent.c
index 554f894..b18dd98 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -35,7 +35,7 @@
 
 #include "includes.h"
 #include "openbsd-compat/sys-queue.h"
-RCSID("$OpenBSD: ssh-agent.c,v 1.106 2003/01/21 18:14:36 marc Exp $");
+RCSID("$OpenBSD: ssh-agent.c,v 1.107 2003/01/23 13:50:27 markus Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/md5.h>
@@ -50,6 +50,8 @@
 #include "authfd.h"
 #include "compat.h"
 #include "log.h"
+#include "readpass.h"
+#include "misc.h"
 
 #ifdef SMARTCARD
 #include "scard.h"
@@ -77,6 +79,7 @@
 	Key *key;
 	char *comment;
 	u_int death;
+	u_int confirm;
 } Identity;
 
 typedef struct {
@@ -162,6 +165,30 @@
 	return (NULL);
 }
 
+/* Check confirmation of keysign request */
+static int
+confirm_key(Identity *id)
+{
+	char *p, prompt[1024];
+	int ret = -1;
+
+	p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX);
+	snprintf(prompt, sizeof(prompt), "Allow use of key %s?\n"
+	    "Key fingerprint %s.", id->comment, p);
+	xfree(p);
+	p = read_passphrase(prompt, RP_ALLOW_EOF);
+	if (p != NULL) {
+		/*
+		 * Accept empty responses and responses consisting 
+		 * of the word "yes" as affirmative.
+		 */
+		if (*p == '\0' || *p == '\n' || strcasecmp(p, "yes") == 0)
+			ret = 0;
+		xfree(p);
+	}
+	return (ret);
+}
+
 /* send list of supported public keys to 'client' */
 static void
 process_request_identities(SocketEntry *e, int version)
@@ -225,7 +252,7 @@
 		goto failure;
 
 	id = lookup_identity(key, 1);
-	if (id != NULL) {
+	if (id != NULL && (!id->confirm || confirm_key(id) == 0)) {
 		Key *private = id->key;
 		/* Decrypt the challenge using the private key. */
 		if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0)
@@ -285,7 +312,7 @@
 	key = key_from_blob(blob, blen);
 	if (key != NULL) {
 		Identity *id = lookup_identity(key, 2);
-		if (id != NULL)
+		if (id != NULL && (!id->confirm || confirm_key(id) == 0))
 			ok = key_sign(id->key, &signature, &slen, data, dlen);
 	}
 	key_free(key);
@@ -405,7 +432,7 @@
 process_add_identity(SocketEntry *e, int version)
 {
 	Idtab *tab = idtab_lookup(version);
-	int type, success = 0, death = 0;
+	int type, success = 0, death = 0, confirm = 0;
 	char *type_name, *comment;
 	Key *k = NULL;
 
@@ -467,6 +494,9 @@
 		case SSH_AGENT_CONSTRAIN_LIFETIME:
 			death = time(NULL) + buffer_get_int(&e->request);
 			break;
+		case SSH_AGENT_CONSTRAIN_CONFIRM:
+			confirm = 1;
+			break;
 		default:
 			break;
 		}
@@ -478,6 +508,7 @@
 		id->key = k;
 		id->comment = comment;
 		id->death = death;
+		id->confirm = confirm;
 		TAILQ_INSERT_TAIL(&tab->idlist, id, next);
 		/* Increment the number of identities. */
 		tab->nentries++;
@@ -562,6 +593,7 @@
 			id->key = k;
 			id->comment = xstrdup("smartcard key");
 			id->death = 0;
+			id->confirm = 0;
 			TAILQ_INSERT_TAIL(&tab->idlist, id, next);
 			tab->nentries++;
 			success = 1;
@@ -942,7 +974,8 @@
 int
 main(int ac, char **av)
 {
-	int sock, c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, ch, nalloc;
+	int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0;
+	int sock, fd,  ch, nalloc;
 	char *shell, *format, *pidstr, *agentsocket = NULL;
 	fd_set *readsetp = NULL, *writesetp = NULL;
 	struct sockaddr_un sunaddr;
@@ -1128,9 +1161,14 @@
 	}
 
 	(void)chdir("/");
-	close(0);
-	close(1);
-	close(2);
+	if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+		/* XXX might close listen socket */
+		(void)dup2(fd, STDIN_FILENO);
+		(void)dup2(fd, STDOUT_FILENO);
+		(void)dup2(fd, STDERR_FILENO);
+		if (fd > 2)
+			close(fd);
+	}
 
 #ifdef HAVE_SETRLIMIT
 	/* deny core dumps, since memory contains unencrypted private keys */