Index | Thread | Search

From:
Damien Miller <djm@mindrot.org>
Subject:
ssh-add: add no-pin flag
To:
tech@openbsd.org
Cc:
openssh@openssh.com
Date:
Fri, 26 Jun 2026 13:44:27 +1000

Download raw body.

Thread
  • Damien Miller:

    ssh-add: add no-pin flag

Hi,

Some FIDO and PKCS#11 tokens have alternate ways of entering PINs or
performing PIN-equivalent authentication, such as biometrics and on-
device PIN entry hardware.

To better support these, this adds a ssh-add -P flag to make PIN entry
optional.

ok?

commit 36b8ed7e63a3ad340f590ad846354b2f8bb1b1f1
Author: Damien Miller <djm@mindrot.org>
Date:   Mon Nov 20 14:31:22 2023 +1100

    Add -P flag to suppress requesting FIDO/p11 PIN

diff --git a/ssh-add.1 b/ssh-add.1
index af5f8f7..e761d21 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -43,7 +43,7 @@
 .Nd adds private key identities to the OpenSSH authentication agent
 .Sh SYNOPSIS
 .Nm ssh-add
-.Op Fl CcDdKkLlNqvXx
+.Op Fl CcDdKkLlNPqvXx
 .Op Fl E Ar fingerprint_hash
 .Op Fl H Ar hostkey_file
 .Op Fl h Ar destination_constraint
@@ -235,6 +235,11 @@ certificates added to an agent.
 .It Fl Q
 Query the agent for the list of protocol extensions it supports.
 Note: not all agents support this query.
+.It Fl P
+When loading PKCS#11 or FIDO keys from a token, do not request a PIN.
+This will cause the operation to fail if the token requires additional
+authentication and has no other way to obtain it, such as a keypad or
+biometric reader.
 .It Fl q
 Be quiet after a successful operation.
 .It Fl S Ar provider
diff --git a/ssh-add.c b/ssh-add.c
index 2788f7e..2fead57 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -452,7 +452,7 @@ add_file(int agent_fd, const char *filename, int key_only, int cert_only,
 }
 
 static int
-update_card(int agent_fd, int add, const char *id, int qflag,
+update_card(int agent_fd, int add, const char *id, int qflag, int no_pin,
     int key_only, int cert_only,
     struct dest_constraint **dest_constraints, size_t ndest_constraints,
     struct sshkey **certs, size_t ncerts)
@@ -463,7 +463,7 @@ update_card(int agent_fd, int add, const char *id, int qflag,
 	if (key_only)
 		ncerts = 0;
 
-	if (add) {
+	if (add && !no_pin) {
 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
 		    RP_ALLOW_STDIN)) == NULL)
 			return -1;
@@ -589,20 +589,24 @@ lock_agent(int agent_fd, int lock)
 }
 
 static int
-load_resident_keys(int agent_fd, const char *skprovider, int qflag,
+load_resident_keys(int agent_fd, const char *skprovider, int qflag, int no_pin,
     struct dest_constraint **dest_constraints, size_t ndest_constraints)
 {
 	struct sshsk_resident_key **srks;
 	size_t nsrks, i;
 	struct sshkey *key;
-	int r, ok = 0;
-	char *fp;
+	int r, ret = -1;
+	char *fp, *pin = NULL;
 
-	pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
-	if ((r = sshsk_load_resident(skprovider, NULL, pass, 0,
-	    &srks, &nsrks)) != 0) {
+	if (!no_pin) {
+		pin = read_passphrase("Enter PIN for authenticator: ",
+		    RP_ALLOW_STDIN);
+	}
+	if ((r = sshsk_load_resident(skprovider, NULL, pin = NULL ? "" : pin,
+	    0, &srks, &nsrks)) != 0) {
 		error_r(r, "Unable to load resident keys");
-		return r;
+		ret = r;
+		goto out;
 	}
 	for (i = 0; i < nsrks; i++) {
 		key = srks[i]->key;
@@ -615,11 +619,9 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
 			error("Unable to add key %s %s",
 			    sshkey_type(key), fp);
 			free(fp);
-			ok = r;
+			ret = r;
 			continue;
 		}
-		if (ok == 0)
-			ok = 1;
 		if (!qflag) {
 			fprintf(stderr, "Resident identity added: %s %s\n",
 			    sshkey_type(key), fp);
@@ -633,11 +635,17 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
 			}
 		}
 		free(fp);
+		if (ret == -1)
+			ret = 0;
 	}
 	sshsk_free_resident_keys(srks, nsrks);
-	if (nsrks == 0)
-		return SSH_ERR_KEY_NOT_FOUND;
-	return ok == 1 ? 0 : ok;
+	if (nsrks == 0) {
+		fprintf(stderr, "No keys loaded from authenticator\n");
+		ret = SSH_ERR_KEY_NOT_FOUND;
+	}
+ out:
+	free(pin);
+	return ret;
 }
 
 static int
@@ -811,7 +819,7 @@ main(int argc, char **argv)
 	char **dest_constraint_strings = NULL, **hostkey_files = NULL;
 	int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
 	int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
-	int Qflag = 0, qflag = 0, Tflag = 0, Nflag = 0;
+	int Qflag = 0, qflag = 0, Tflag = 0, Nflag = 0, no_pin = 0;
 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
 	LogLevel log_level = SYSLOG_LEVEL_INFO;
 	struct sshkey *k, **certs = NULL;
@@ -840,7 +848,7 @@ main(int argc, char **argv)
 
 	skprovider = getenv("SSH_SK_PROVIDER");
 
-	while ((ch = getopt(argc, argv, "vkKlLNCcdDTxXE:e:h:H:M:m:Qqs:S:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "vkKlLNPCcdDTxXE:e:h:H:M:m:Qqs:S:t:")) != -1) {
 		switch (ch) {
 		case 'v':
 			if (log_level == SYSLOG_LEVEL_INFO)
@@ -893,6 +901,9 @@ main(int argc, char **argv)
 		case 'd':
 			deleting = 1;
 			break;
+		case 'P':
+			no_pin = 1;
+			break;
 		case 'D':
 			Dflag = 1;
 			break;
@@ -988,7 +999,7 @@ main(int argc, char **argv)
 		}
 		debug2_f("loaded %zu certificates", ncerts);
 		if (update_card(agent_fd, !deleting, pkcs11provider,
-		    qflag, key_only, cert_only,
+		    qflag, no_pin, key_only, cert_only,
 		    dest_constraints, ndest_constraints,
 		    certs, ncerts) == -1)
 			ret = 1;
@@ -1000,7 +1011,7 @@ main(int argc, char **argv)
 	if (do_download) {
 		if (skprovider == NULL)
 			fatal("Cannot download keys without provider");
-		if (load_resident_keys(agent_fd, skprovider, qflag,
+		if (load_resident_keys(agent_fd, skprovider, qflag, no_pin,
 		    dest_constraints, ndest_constraints) != 0)
 			ret = 1;
 		goto done;