upstream: support for user-verified FIDO keys

FIDO2 supports a notion of "user verification" where the user is
required to demonstrate their identity to the token before particular
operations (e.g. signing). Typically this is done by authenticating
themselves using a PIN that has been set on the token.

This adds support for generating and using user verified keys where
the verification happens via PIN (other options might be added in the
future, but none are in common use now). Practically, this adds
another key generation option "verify-required" that yields a key that
requires a PIN before each authentication.

feedback markus@ and Pedro Martelletto; ok markus@

OpenBSD-Commit-ID: 57fd461e4366f87c47502c5614ec08573e6d6a15
This commit is contained in:
djm@openbsd.org 2020-08-27 01:06:18 +00:00 committed by Damien Miller
parent 1196d7f49d
commit 9b8ad93824
16 changed files with 164 additions and 90 deletions

7
krl.c
View File

@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $OpenBSD: krl.c,v 1.50 2020/04/03 05:48:57 djm Exp $ */ /* $OpenBSD: krl.c,v 1.51 2020/08/27 01:06:18 djm Exp $ */
#include "includes.h" #include "includes.h"
@ -812,9 +812,10 @@ ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 ||
(r = sshkey_puts(sign_keys[i], buf)) != 0) (r = sshkey_puts(sign_keys[i], buf)) != 0)
goto out; goto out;
/* XXX support sk-* keys */
if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, if ((r = sshkey_sign(sign_keys[i], &sblob, &slen,
sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL, 0)) != 0) sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL,
NULL, 0)) != 0)
goto out; goto out;
KRL_DBG(("%s: signature sig len %zu", __func__, slen)); KRL_DBG(("%s: signature sig len %zu", __func__, slen));
if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) if ((r = sshbuf_put_string(buf, sblob, slen)) != 0)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.212 2020/07/07 02:47:21 deraadt Exp $ */ /* $OpenBSD: monitor.c,v 1.213 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -679,7 +679,7 @@ mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m)
if ((key = get_hostkey_by_index(keyid)) != NULL) { if ((key = get_hostkey_by_index(keyid)) != NULL) {
if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg,
options.sk_provider, compat)) != 0) options.sk_provider, NULL, compat)) != 0)
fatal("%s: sshkey_sign failed: %s", fatal("%s: sshkey_sign failed: %s",
__func__, ssh_err(r)); __func__, ssh_err(r));
} else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL &&

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor_wrap.c,v 1.117 2019/12/15 18:57:30 djm Exp $ */ /* $OpenBSD: monitor_wrap.c,v 1.118 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -216,7 +216,7 @@ mm_choose_dh(int min, int nbits, int max)
int int
mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const char *hostkey_alg, const u_char *data, size_t datalen, const char *hostkey_alg,
const char *sk_provider, u_int compat) const char *sk_provider, const char *sk_pin, u_int compat)
{ {
struct kex *kex = *pmonitor->m_pkex; struct kex *kex = *pmonitor->m_pkex;
struct sshbuf *m; struct sshbuf *m;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor_wrap.h,v 1.44 2019/11/25 00:51:37 djm Exp $ */ /* $OpenBSD: monitor_wrap.h,v 1.45 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@ -46,7 +46,8 @@ int mm_is_monitor(void);
DH *mm_choose_dh(int, int, int); DH *mm_choose_dh(int, int, int);
#endif #endif
int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *, int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, const char *, u_int compat); const u_char *, size_t, const char *, const char *,
const char *, u_int compat);
void mm_inform_authserv(char *, char *); void mm_inform_authserv(char *, char *);
struct passwd *mm_getpwnamallow(struct ssh *, const char *); struct passwd *mm_getpwnamallow(struct ssh *, const char *);
char *mm_auth2_read_banner(void); char *mm_auth2_read_banner(void);

View File

@ -163,7 +163,8 @@ pick_first_device(void)
/* Check if the specified key handle exists on a given device. */ /* Check if the specified key handle exists on a given device. */
static int static int
try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
const char *application, const uint8_t *key_handle, size_t key_handle_len) const char *application, const uint8_t *key_handle, size_t key_handle_len,
uint8_t flags, const char *pin)
{ {
fido_assert_t *assert = NULL; fido_assert_t *assert = NULL;
int r = FIDO_ERR_INTERNAL; int r = FIDO_ERR_INTERNAL;
@ -191,7 +192,7 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
goto out; goto out;
} }
r = fido_dev_get_assert(dev, assert, NULL); r = fido_dev_get_assert(dev, assert, pin);
skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
/* U2F tokens may return this */ /* U2F tokens may return this */
@ -206,7 +207,8 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
/* Iterate over configured devices looking for a specific key handle */ /* Iterate over configured devices looking for a specific key handle */
static fido_dev_t * static fido_dev_t *
find_device(const char *path, const uint8_t *message, size_t message_len, find_device(const char *path, const uint8_t *message, size_t message_len,
const char *application, const uint8_t *key_handle, size_t key_handle_len) const char *application, const uint8_t *key_handle, size_t key_handle_len,
uint8_t flags, const char *pin)
{ {
fido_dev_info_t *devlist = NULL; fido_dev_info_t *devlist = NULL;
fido_dev_t *dev = NULL; fido_dev_t *dev = NULL;
@ -260,7 +262,7 @@ find_device(const char *path, const uint8_t *message, size_t message_len,
continue; continue;
} }
if (try_device(dev, message, message_len, application, if (try_device(dev, message, message_len, application,
key_handle, key_handle_len) == 0) { key_handle, key_handle_len, flags, pin) == 0) {
skdebug(__func__, "found key"); skdebug(__func__, "found key");
break; break;
} }
@ -570,19 +572,23 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
goto out; goto out;
} }
if ((flags & SSH_SK_RESIDENT_KEY) != 0) { if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
if (check_sk_extensions(dev, "credProtect", &credprot) < 0) { if (check_sk_extensions(dev, "credProtect", &credprot) < 0) {
skdebug(__func__, "check_sk_extensions failed"); skdebug(__func__, "check_sk_extensions failed");
goto out; goto out;
} }
if (credprot == 0) { if (credprot == 0) {
skdebug(__func__, "refusing to create unprotected " skdebug(__func__, "refusing to create unprotected "
"resident key"); "resident/verify-required key");
ret = SSH_SK_ERR_UNSUPPORTED; ret = SSH_SK_ERR_UNSUPPORTED;
goto out; goto out;
} }
if ((r = fido_cred_set_prot(cred, if ((flags & SSH_SK_USER_VERIFICATION_REQD))
FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID)) != FIDO_OK) { credprot = FIDO_CRED_PROT_UV_REQUIRED;
else
credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
skdebug(__func__, "fido_cred_set_prot: %s", skdebug(__func__, "fido_cred_set_prot: %s",
fido_strerr(r)); fido_strerr(r));
ret = fidoerr_to_skerr(r); ret = fidoerr_to_skerr(r);
@ -826,7 +832,7 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
goto out; goto out;
} }
if ((dev = find_device(device, message, sizeof(message), if ((dev = find_device(device, message, sizeof(message),
application, key_handle, key_handle_len)) == NULL) { application, key_handle, key_handle_len, flags, pin)) == NULL) {
skdebug(__func__, "couldn't find device for key handle"); skdebug(__func__, "couldn't find device for key handle");
goto out; goto out;
} }
@ -855,8 +861,15 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
goto out; goto out;
} }
if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD) &&
(r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) {
skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
ret = FIDO_ERR_PIN_REQUIRED;
goto out;
}
if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
ret = fidoerr_to_skerr(r);
goto out; goto out;
} }
if ((response = calloc(1, sizeof(*response))) == NULL) { if ((response = calloc(1, sizeof(*response))) == NULL) {
@ -978,8 +991,9 @@ read_rks(const char *devpath, const char *pin,
continue; continue;
} }
skdebug(__func__, "Device %s RP \"%s\" slot %zu: " skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
"type %d", devpath, fido_credman_rp_id(rp, i), j, "type %d flags 0x%02x prot 0x%02x", devpath,
fido_cred_type(cred)); fido_credman_rp_id(rp, i), j, fido_cred_type(cred),
fido_cred_flags(cred), fido_cred_prot(cred));
/* build response entry */ /* build response entry */
if ((srk = calloc(1, sizeof(*srk))) == NULL || if ((srk = calloc(1, sizeof(*srk))) == NULL ||

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.262 2020/07/05 23:59:45 djm Exp $ */ /* $OpenBSD: ssh-agent.c,v 1.263 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -407,9 +407,10 @@ process_sign_request2(SocketEntry *e)
sshkey_type(id->key), fp); sshkey_type(id->key), fp);
} }
} }
/* XXX support PIN required FIDO keys */
if ((r = sshkey_sign(id->key, &signature, &slen, if ((r = sshkey_sign(id->key, &signature, &slen,
data, dlen, agent_decode_alg(key, flags), data, dlen, agent_decode_alg(key, flags),
id->sk_provider, compat)) != 0) { id->sk_provider, NULL, compat)) != 0) {
error("%s: sshkey_sign: %s", __func__, ssh_err(r)); error("%s: sshkey_sign: %s", __func__, ssh_err(r));
goto send; goto send;
} }

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.205 2020/07/15 07:50:46 solene Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.206 2020/08/27 01:06:18 djm Exp $
.\" .\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -35,7 +35,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: July 15 2020 $ .Dd $Mdocdate: August 27 2020 $
.Dt SSH-KEYGEN 1 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -511,6 +511,12 @@ A username to be associated with a resident key,
overriding the empty default username. overriding the empty default username.
Specifying a username may be useful when generating multiple resident keys Specifying a username may be useful when generating multiple resident keys
for the same application name. for the same application name.
.It Cm verify-required
Indicate that this private key should require user verification for
each signature.
Not all FIDO tokens support support this option.
Currently PIN authentication is the only supported verification method,
but other methods may be supported in the future.
.It Cm write-attestation Ns = Ns Ar path .It Cm write-attestation Ns = Ns Ar path
May be used at key generation time to record the attestation certificate May be used at key generation time to record the attestation certificate
returned from FIDO tokens during key generation. returned from FIDO tokens during key generation.
@ -961,7 +967,7 @@ by
Allows X11 forwarding. Allows X11 forwarding.
.Pp .Pp
.It Ic no-touch-required .It Ic no-touch-required
Do not require signatures made using this key require demonstration Do not require signatures made using this key include demonstration
of user presence (e.g. by having the user touch the authenticator). of user presence (e.g. by having the user touch the authenticator).
This option only makes sense for the FIDO authenticator algorithms This option only makes sense for the FIDO authenticator algorithms
.Cm ecdsa-sk .Cm ecdsa-sk
@ -974,6 +980,16 @@ The
.Ar address_list .Ar address_list
is a comma-separated list of one or more address/netmask pairs in CIDR is a comma-separated list of one or more address/netmask pairs in CIDR
format. format.
.Pp
.It Ic verify-required
Require signatures made using this key indicate that the user was first
verified.
This option only makes sense for the FIDO authenticator algorithms
.Cm ecdsa-sk
and
.Cm ed25519-sk .
Currently PIN authentication is the only supported verification method,
but other methods may be supported in the future.
.El .El
.Pp .Pp
At present, no standard options are valid for host keys. At present, no standard options are valid for host keys.

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.415 2020/08/03 02:53:51 djm Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.416 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -589,7 +589,7 @@ do_convert_private_ssh2(struct sshbuf *b)
/* try the key */ /* try the key */
if (sshkey_sign(key, &sig, &slen, data, sizeof(data), if (sshkey_sign(key, &sig, &slen, data, sizeof(data),
NULL, NULL, 0) != 0 || NULL, NULL, NULL, 0) != 0 ||
sshkey_verify(key, sig, slen, data, sizeof(data), sshkey_verify(key, sig, slen, data, sizeof(data),
NULL, 0, NULL) != 0) { NULL, 0, NULL) != 0) {
sshkey_free(key); sshkey_free(key);
@ -1727,7 +1727,8 @@ load_pkcs11_key(char *path)
static int static int
agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const u_char *data, size_t datalen,
const char *alg, const char *provider, u_int compat, void *ctx) const char *alg, const char *provider, const char *pin,
u_int compat, void *ctx)
{ {
int *agent_fdp = (int *)ctx; int *agent_fdp = (int *)ctx;
@ -1744,7 +1745,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
u_int n; u_int n;
struct sshkey *ca, *public; struct sshkey *ca, *public;
char valid[64], *otmp, *tmp, *cp, *out, *comment; char valid[64], *otmp, *tmp, *cp, *out, *comment;
char *ca_fp = NULL, **plist = NULL; char *ca_fp = NULL, **plist = NULL, *pin = NULL;
struct ssh_identitylist *agent_ids; struct ssh_identitylist *agent_ids;
size_t j; size_t j;
struct notifier_ctx *notifier = NULL; struct notifier_ctx *notifier = NULL;
@ -1785,6 +1786,12 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
} else { } else {
/* CA key is assumed to be a private key on the filesystem */ /* CA key is assumed to be a private key on the filesystem */
ca = load_identity(tmp, NULL); ca = load_identity(tmp, NULL);
if (sshkey_is_sk(ca) &&
(ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
if ((pin = read_passphrase("Enter PIN for CA key: ",
RP_ALLOW_STDIN)) == NULL)
fatal("%s: couldn't read PIN", __func__);
}
} }
free(tmp); free(tmp);
@ -1844,7 +1851,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
if ((r = sshkey_certify_custom(public, ca, if ((r = sshkey_certify_custom(public, ca,
key_type_name, sk_provider, agent_signer, key_type_name, sk_provider, NULL, agent_signer,
&agent_fd)) != 0) &agent_fd)) != 0)
fatal("Couldn't certify key %s via agent: %s", fatal("Couldn't certify key %s via agent: %s",
tmp, ssh_err(r)); tmp, ssh_err(r));
@ -1856,7 +1863,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
sshkey_type(ca), ca_fp); sshkey_type(ca), ca_fp);
} }
r = sshkey_certify(public, ca, key_type_name, r = sshkey_certify(public, ca, key_type_name,
sk_provider); sk_provider, pin);
notify_complete(notifier); notify_complete(notifier);
if (r != 0) if (r != 0)
fatal("Couldn't certify key %s: %s", fatal("Couldn't certify key %s: %s",
@ -1890,6 +1897,8 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
if (cert_serial_autoinc) if (cert_serial_autoinc)
cert_serial++; cert_serial++;
} }
if (pin != NULL)
freezero(pin, strlen(pin));
free(ca_fp); free(ca_fp);
#ifdef ENABLE_PKCS11 #ifdef ENABLE_PKCS11
pkcs11_terminate(); pkcs11_terminate();
@ -2526,6 +2535,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshbuf *sigbuf = NULL, *abuf = NULL;
int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno;
char *wfile = NULL, *asig = NULL, *fp = NULL; char *wfile = NULL, *asig = NULL, *fp = NULL;
char *pin = NULL, *prompt = NULL;
if (!quiet) { if (!quiet) {
if (fd == STDIN_FILENO) if (fd == STDIN_FILENO)
@ -2533,17 +2543,25 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
else else
fprintf(stderr, "Signing file %s\n", filename); fprintf(stderr, "Signing file %s\n", filename);
} }
if (signer == NULL && sshkey_is_sk(signkey) && if (signer == NULL && sshkey_is_sk(signkey)) {
(signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, xasprintf(&prompt, "Enter PIN for %s key: ",
SSH_FP_DEFAULT)) == NULL) sshkey_type(signkey));
fatal("%s: sshkey_fingerprint failed", __func__); if ((pin = read_passphrase(prompt,
fprintf(stderr, "Confirm user presence for key %s %s\n", RP_ALLOW_STDIN)) == NULL)
sshkey_type(signkey), fp); fatal("%s: couldn't read PIN", __func__);
free(fp); }
if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
if ((fp = sshkey_fingerprint(signkey, fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
fatal("%s: fingerprint failed", __func__);
fprintf(stderr, "Confirm user presence for key %s %s\n",
sshkey_type(signkey), fp);
free(fp);
}
} }
if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, fd, sig_namespace, if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, pin,
&sigbuf, signer, signer_ctx)) != 0) { fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) {
error("Signing %s failed: %s", filename, ssh_err(r)); error("Signing %s failed: %s", filename, ssh_err(r));
goto out; goto out;
} }
@ -2591,7 +2609,10 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
r = 0; r = 0;
out: out:
free(wfile); free(wfile);
free(prompt);
free(asig); free(asig);
if (pin != NULL)
freezero(pin, strlen(pin));
sshbuf_free(abuf); sshbuf_free(abuf);
sshbuf_free(sigbuf); sshbuf_free(sigbuf);
if (wfd != -1) if (wfd != -1)
@ -3554,6 +3575,8 @@ main(int argc, char **argv)
for (i = 0; i < nopts; i++) { for (i = 0; i < nopts; i++) {
if (strcasecmp(opts[i], "no-touch-required") == 0) { if (strcasecmp(opts[i], "no-touch-required") == 0) {
sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
} else if (strcasecmp(opts[i], "verify-required") == 0) {
sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
} else if (strcasecmp(opts[i], "resident") == 0) { } else if (strcasecmp(opts[i], "resident") == 0) {
sk_flags |= SSH_SK_RESIDENT_KEY; sk_flags |= SSH_SK_RESIDENT_KEY;
} else if (strncasecmp(opts[i], "device=", 7) == 0) { } else if (strncasecmp(opts[i], "device=", 7) == 0) {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keysign.c,v 1.63 2019/11/18 16:10:05 naddy Exp $ */ /* $OpenBSD: ssh-keysign.c,v 1.64 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright (c) 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Markus Friedl. All rights reserved.
* *
@ -278,7 +278,7 @@ main(int argc, char **argv)
} }
if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen,
NULL, NULL, 0)) != 0) NULL, NULL, NULL, 0)) != 0)
fatal("sshkey_sign failed: %s", ssh_err(r)); fatal("sshkey_sign failed: %s", ssh_err(r));
free(data); free(data);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh_api.c,v 1.20 2020/07/01 16:28:31 markus Exp $ */ /* $OpenBSD: ssh_api.c,v 1.21 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright (c) 2012 Markus Friedl. All rights reserved. * Copyright (c) 2012 Markus Friedl. All rights reserved.
* *
@ -54,7 +54,7 @@ int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *,
*/ */
int use_privsep = 0; int use_privsep = 0;
int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, int mm_sshkey_sign(struct sshkey *, u_char **, u_int *,
const u_char *, u_int, const char *, const char *, u_int); const u_char *, u_int, const char *, const char *, const char *, u_int);
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL
DH *mm_choose_dh(int, int, int); DH *mm_choose_dh(int, int, int);
@ -66,8 +66,8 @@ u_int session_id2_len = 0;
int int
mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp,
const u_char *data, u_int datalen, const char *alg, const char *sk_provider, const u_char *data, u_int datalen, const char *alg,
u_int compat) const char *sk_provider, const char *sk_pin, u_int compat)
{ {
return (-1); return (-1);
} }
@ -567,5 +567,5 @@ _ssh_host_key_sign(struct ssh *ssh, struct sshkey *privkey,
const u_char *data, size_t dlen, const char *alg) const u_char *data, size_t dlen, const char *alg)
{ {
return sshkey_sign(privkey, signature, slen, data, dlen, return sshkey_sign(privkey, signature, slen, data, dlen,
alg, NULL, ssh->compat); alg, NULL, NULL, ssh->compat);
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect2.c,v 1.324 2020/06/27 13:39:09 bket Exp $ */ /* $OpenBSD: sshconnect2.c,v 1.325 2020/08/27 01:06:18 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved.
@ -1175,7 +1175,7 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
struct sshkey *sign_key = NULL, *prv = NULL; struct sshkey *sign_key = NULL, *prv = NULL;
int r = SSH_ERR_INTERNAL_ERROR; int r = SSH_ERR_INTERNAL_ERROR;
struct notifier_ctx *notifier = NULL; struct notifier_ctx *notifier = NULL;
char *fp = NULL; char *fp = NULL, *pin = NULL, *prompt = NULL;
*sigp = NULL; *sigp = NULL;
*lenp = 0; *lenp = 0;
@ -1204,20 +1204,28 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
goto out; goto out;
} }
sign_key = prv; sign_key = prv;
if (sshkey_is_sk(sign_key) && if (sshkey_is_sk(sign_key)) {
(sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { if ((sign_key->sk_flags &
/* XXX match batch mode should just skip these keys? */ SSH_SK_USER_VERIFICATION_REQD)) {
if ((fp = sshkey_fingerprint(sign_key, xasprintf(&prompt, "Enter PIN for %s key %s: ",
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) sshkey_type(sign_key), id->filename);
fatal("%s: sshkey_fingerprint", __func__); pin = read_passphrase(prompt, 0);
notifier = notify_start(options.batch_mode, }
"Confirm user presence for key %s %s", if ((sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
sshkey_type(sign_key), fp); /* XXX should batch mode just skip these? */
free(fp); if ((fp = sshkey_fingerprint(sign_key,
options.fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
fatal("%s: fingerprint", __func__);
notifier = notify_start(options.batch_mode,
"Confirm user presence for key %s %s",
sshkey_type(sign_key), fp);
free(fp);
}
} }
} }
if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen,
alg, options.sk_provider, compat)) != 0) { alg, options.sk_provider, pin, compat)) != 0) {
debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
goto out; goto out;
} }
@ -1232,6 +1240,9 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
/* success */ /* success */
r = 0; r = 0;
out: out:
free(prompt);
if (pin != NULL)
freezero(pin, strlen(pin));
notify_complete(notifier); notify_complete(notifier);
sshkey_free(prv); sshkey_free(prv);
return r; return r;

8
sshd.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.560 2020/07/03 10:12:26 markus Exp $ */ /* $OpenBSD: sshd.c,v 1.561 2020/08/27 01:06:19 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2338,19 +2338,19 @@ sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
if (use_privsep) { if (use_privsep) {
if (privkey) { if (privkey) {
if (mm_sshkey_sign(ssh, privkey, signature, slenp, if (mm_sshkey_sign(ssh, privkey, signature, slenp,
data, dlen, alg, options.sk_provider, data, dlen, alg, options.sk_provider, NULL,
ssh->compat) < 0) ssh->compat) < 0)
fatal("%s: privkey sign failed", __func__); fatal("%s: privkey sign failed", __func__);
} else { } else {
if (mm_sshkey_sign(ssh, pubkey, signature, slenp, if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
data, dlen, alg, options.sk_provider, data, dlen, alg, options.sk_provider, NULL,
ssh->compat) < 0) ssh->compat) < 0)
fatal("%s: pubkey sign failed", __func__); fatal("%s: pubkey sign failed", __func__);
} }
} else { } else {
if (privkey) { if (privkey) {
if (sshkey_sign(privkey, signature, slenp, data, dlen, if (sshkey_sign(privkey, signature, slenp, data, dlen,
alg, options.sk_provider, ssh->compat) < 0) alg, options.sk_provider, NULL, ssh->compat) < 0)
fatal("%s: privkey sign failed", __func__); fatal("%s: privkey sign failed", __func__);
} else { } else {
if ((r = ssh_agent_sign(auth_sock, pubkey, if ((r = ssh_agent_sign(auth_sock, pubkey,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.110 2020/06/24 15:07:33 markus Exp $ */ /* $OpenBSD: sshkey.c,v 1.111 2020/08/27 01:06:19 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -2727,7 +2727,7 @@ int
sshkey_sign(struct sshkey *key, sshkey_sign(struct sshkey *key,
u_char **sigp, size_t *lenp, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const u_char *data, size_t datalen,
const char *alg, const char *sk_provider, u_int compat) const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
{ {
int was_shielded = sshkey_is_shielded(key); int was_shielded = sshkey_is_shielded(key);
int r2, r = SSH_ERR_INTERNAL_ERROR; int r2, r = SSH_ERR_INTERNAL_ERROR;
@ -2766,7 +2766,7 @@ sshkey_sign(struct sshkey *key,
case KEY_ECDSA_SK_CERT: case KEY_ECDSA_SK_CERT:
case KEY_ECDSA_SK: case KEY_ECDSA_SK:
r = sshsk_sign(sk_provider, key, sigp, lenp, data, r = sshsk_sign(sk_provider, key, sigp, lenp, data,
datalen, compat, /* XXX PIN */ NULL); datalen, compat, sk_pin);
break; break;
#ifdef WITH_XMSS #ifdef WITH_XMSS
case KEY_XMSS: case KEY_XMSS:
@ -2888,7 +2888,8 @@ sshkey_drop_cert(struct sshkey *k)
/* Sign a certified key, (re-)generating the signed certblob. */ /* Sign a certified key, (re-)generating the signed certblob. */
int int
sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
const char *sk_provider, sshkey_certify_signer *signer, void *signer_ctx) const char *sk_provider, const char *sk_pin,
sshkey_certify_signer *signer, void *signer_ctx)
{ {
struct sshbuf *principals = NULL; struct sshbuf *principals = NULL;
u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
@ -3026,7 +3027,7 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
/* Sign the whole mess */ /* Sign the whole mess */
if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
sshbuf_len(cert), alg, sk_provider, 0, signer_ctx)) != 0) sshbuf_len(cert), alg, sk_provider, sk_pin, 0, signer_ctx)) != 0)
goto out; goto out;
/* Check and update signature_type against what was actually used */ /* Check and update signature_type against what was actually used */
if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0) if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0)
@ -3056,19 +3057,20 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
static int static int
default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp, default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const u_char *data, size_t datalen,
const char *alg, const char *sk_provider, u_int compat, void *ctx) const char *alg, const char *sk_provider, const char *sk_pin,
u_int compat, void *ctx)
{ {
if (ctx != NULL) if (ctx != NULL)
return SSH_ERR_INVALID_ARGUMENT; return SSH_ERR_INVALID_ARGUMENT;
return sshkey_sign(key, sigp, lenp, data, datalen, alg, return sshkey_sign(key, sigp, lenp, data, datalen, alg,
sk_provider, compat); sk_provider, sk_pin, compat);
} }
int int
sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg, sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg,
const char *sk_provider) const char *sk_provider, const char *sk_pin)
{ {
return sshkey_certify_custom(k, ca, alg, sk_provider, return sshkey_certify_custom(k, ca, alg, sk_provider, sk_pin,
default_key_sign, NULL); default_key_sign, NULL);
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.45 2020/04/08 00:08:46 djm Exp $ */ /* $OpenBSD: sshkey.h,v 1.46 2020/08/27 01:06:19 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -200,12 +200,13 @@ size_t sshkey_format_cert_validity(const struct sshkey_cert *,
int sshkey_check_cert_sigtype(const struct sshkey *, const char *); int sshkey_check_cert_sigtype(const struct sshkey *, const char *);
int sshkey_certify(struct sshkey *, struct sshkey *, int sshkey_certify(struct sshkey *, struct sshkey *,
const char *, const char *); const char *, const char *, const char *);
/* Variant allowing use of a custom signature function (e.g. for ssh-agent) */ /* Variant allowing use of a custom signature function (e.g. for ssh-agent) */
typedef int sshkey_certify_signer(struct sshkey *, u_char **, size_t *, typedef int sshkey_certify_signer(struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, const char *, u_int, void *); const u_char *, size_t, const char *, const char *, const char *,
u_int, void *);
int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *, int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *,
const char *, sshkey_certify_signer *, void *); const char *, const char *, sshkey_certify_signer *, void *);
int sshkey_ecdsa_nid_from_name(const char *); int sshkey_ecdsa_nid_from_name(const char *);
int sshkey_curve_name_to_nid(const char *); int sshkey_curve_name_to_nid(const char *);
@ -234,7 +235,7 @@ int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); int sshkey_putb_plain(const struct sshkey *, struct sshbuf *);
int sshkey_sign(struct sshkey *, u_char **, size_t *, int sshkey_sign(struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, const char *, u_int); const u_char *, size_t, const char *, const char *, const char *, u_int);
int sshkey_verify(const struct sshkey *, const u_char *, size_t, int sshkey_verify(const struct sshkey *, const u_char *, size_t,
const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **); const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
int sshkey_check_sigtype(const u_char *, size_t, const char *); int sshkey_check_sigtype(const u_char *, size_t, const char *);

View File

@ -151,7 +151,7 @@ done:
static int static int
sshsig_wrap_sign(struct sshkey *key, const char *hashalg, sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
const char *sk_provider, const struct sshbuf *h_message, const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
const char *sig_namespace, struct sshbuf **out, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx) sshsig_signer *signer, void *signer_ctx)
{ {
@ -185,14 +185,14 @@ sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
if (signer != NULL) { if (signer != NULL) {
if ((r = signer(key, &sig, &slen, if ((r = signer(key, &sig, &slen,
sshbuf_ptr(tosign), sshbuf_len(tosign), sshbuf_ptr(tosign), sshbuf_len(tosign),
sign_alg, sk_provider, 0, signer_ctx)) != 0) { sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
error("Couldn't sign message: %s", ssh_err(r)); error("Couldn't sign message: %s", ssh_err(r));
goto done; goto done;
} }
} else { } else {
if ((r = sshkey_sign(key, &sig, &slen, if ((r = sshkey_sign(key, &sig, &slen,
sshbuf_ptr(tosign), sshbuf_len(tosign), sshbuf_ptr(tosign), sshbuf_len(tosign),
sign_alg, sk_provider, 0)) != 0) { sign_alg, sk_provider, sk_pin, 0)) != 0) {
error("Couldn't sign message: %s", ssh_err(r)); error("Couldn't sign message: %s", ssh_err(r));
goto done; goto done;
} }
@ -430,7 +430,8 @@ hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
} }
int int
sshsig_signb(struct sshkey *key, const char *hashalg, const char *sk_provider, sshsig_signb(struct sshkey *key, const char *hashalg,
const char *sk_provider, const char *sk_pin,
const struct sshbuf *message, const char *sig_namespace, const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
{ {
@ -445,7 +446,7 @@ sshsig_signb(struct sshkey *key, const char *hashalg, const char *sk_provider,
error("%s: hash_buffer failed: %s", __func__, ssh_err(r)); error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
goto out; goto out;
} }
if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b, if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
sig_namespace, out, signer, signer_ctx)) != 0) sig_namespace, out, signer, signer_ctx)) != 0)
goto out; goto out;
/* success */ /* success */
@ -558,7 +559,8 @@ hash_file(int fd, const char *hashalg, struct sshbuf **bp)
} }
int int
sshsig_sign_fd(struct sshkey *key, const char *hashalg, const char *sk_provider, sshsig_sign_fd(struct sshkey *key, const char *hashalg,
const char *sk_provider, const char *sk_pin,
int fd, const char *sig_namespace, struct sshbuf **out, int fd, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx) sshsig_signer *signer, void *signer_ctx)
{ {
@ -573,7 +575,7 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg, const char *sk_provider,
error("%s: hash_file failed: %s", __func__, ssh_err(r)); error("%s: hash_file failed: %s", __func__, ssh_err(r));
return r; return r;
} }
if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b, if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
sig_namespace, out, signer, signer_ctx)) != 0) sig_namespace, out, signer, signer_ctx)) != 0)
goto out; goto out;
/* success */ /* success */

View File

@ -23,7 +23,8 @@ struct sshsigopt;
struct sshkey_sig_details; struct sshkey_sig_details;
typedef int sshsig_signer(struct sshkey *, u_char **, size_t *, typedef int sshsig_signer(struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, const char *, u_int, void *); const u_char *, size_t, const char *, const char *, const char *,
u_int, void *);
/* Buffer-oriented API */ /* Buffer-oriented API */
@ -33,7 +34,7 @@ typedef int sshsig_signer(struct sshkey *, u_char **, size_t *,
* out is populated with the detached signature, or NULL on failure. * out is populated with the detached signature, or NULL on failure.
*/ */
int sshsig_signb(struct sshkey *key, const char *hashalg, int sshsig_signb(struct sshkey *key, const char *hashalg,
const char *sk_provider, const struct sshbuf *message, const char *sk_provider, const char *sk_pin, const struct sshbuf *message,
const char *sig_namespace, struct sshbuf **out, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx); sshsig_signer *signer, void *signer_ctx);
@ -54,7 +55,8 @@ int sshsig_verifyb(struct sshbuf *signature,
* out is populated with the detached signature, or NULL on failure. * out is populated with the detached signature, or NULL on failure.
*/ */
int sshsig_sign_fd(struct sshkey *key, const char *hashalg, int sshsig_sign_fd(struct sshkey *key, const char *hashalg,
const char *sk_provider, int fd, const char *sig_namespace, const char *sk_provider, const char *sk_pin,
int fd, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx); struct sshbuf **out, sshsig_signer *signer, void *signer_ctx);
/* /*