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