From 07da39f71d36fb547749a5b16aa8892e621a7e4a Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 31 Oct 2019 21:22:01 +0000 Subject: upstream: ssh-agent support for U2F/FIDO keys feedback & ok markus@ OpenBSD-Commit-ID: bb544a44bc32e45d2ec8bf652db2046f38360acb --- ssh-sk-helper.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 ssh-sk-helper.c (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c new file mode 100644 index 000000000..0a0c92a44 --- /dev/null +++ b/ssh-sk-helper.c @@ -0,0 +1,143 @@ +/* $OpenBSD: ssh-sk-helper.c,v 1.1 2019/10/31 21:22:01 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is a tiny program used to isolate the address space used for + * security key middleware signing operations from ssh-agent. It is similar + * to ssh-pkcs11-helper.c but considerably simpler as the signing operation + * for this case are stateless. + * + * It receives a signing request (key, provider, message, flags) from + * stdin, attempts to perform a signature using the security key provider + * and returns the resultant signature via stdout. + * + * In the future, this program might gain additional functions to support + * FIDO2 tokens such as enumerating resident keys. When this happens it will + * be necessary to crank SSH_SK_HELPER_VERSION below. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "log.h" +#include "sshkey.h" +#include "authfd.h" +#include "misc.h" +#include "sshbuf.h" +#include "msg.h" +#include "uidswap.h" +#include "sshkey.h" +#include "ssherr.h" +#include "ssh-sk.h" + +extern char *__progname; + +int +main(int argc, char **argv) +{ + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; + struct sshbuf *req, *resp, *kbuf; + struct sshkey *key; + uint32_t compat; + const u_char *message; + u_char version, *sig; + size_t msglen, siglen; + char *provider; + int in, out, ch, r, log_stderr = 0; + + sanitise_stdfd(); + log_init(__progname, log_level, log_facility, log_stderr); + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + log_stderr = 1; + if (log_level == SYSLOG_LEVEL_ERROR) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + break; + default: + fprintf(stderr, "usage: %s [-v]\n", __progname); + exit(1); + } + } + log_init(__progname, log_level, log_facility, log_stderr); + + /* + * Rearrange our file descriptors a little; we don't trust the + * providers not to fiddle with stdin/out. + */ + closefrom(STDERR_FILENO + 1); + if ((in = dup(STDIN_FILENO)) == -1 || (out = dup(STDOUT_FILENO)) == -1) + fatal("%s: dup: %s", __progname, strerror(errno)); + close(STDIN_FILENO); + close(STDOUT_FILENO); + sanitise_stdfd(); /* resets to /dev/null */ + + if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if (ssh_msg_recv(in, req) < 0) + fatal("ssh_msg_recv failed"); + close(in); + debug("%s: received message len %zu", __progname, sshbuf_len(req)); + + if ((r = sshbuf_get_u8(req, &version)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (version != SSH_SK_HELPER_VERSION) { + fatal("unsupported version: received %d, expected %d", + version, SSH_SK_HELPER_VERSION); + } + if ((r = sshbuf_froms(req, &kbuf)) != 0 || + (r = sshkey_private_deserialize(kbuf, &key)) != 0) + fatal("Unable to parse key: %s", ssh_err(r)); + if (sshkey_type_plain(key->type) != KEY_ECDSA_SK) + fatal("Unsupported key type %s", sshkey_ssh_name(key)); + + if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || + (r = sshbuf_get_u32(req, &compat)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + debug("%s: ready to sign with key %s, provider %s: " + "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), + provider, msglen, (u_long)compat); + + if ((r = sshsk_ecdsa_sign(provider, key, &sig, &siglen, + message, msglen, compat)) != 0) + fatal("Signing failed: %s", ssh_err(r)); + + /* send reply */ + if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + debug("%s: reply len %zu", __progname, sshbuf_len(resp)); + if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1) + fatal("ssh_msg_send failed"); + close(out); + + return (0); +} -- cgit v1.2.3 From 764d51e04460ec0da12e05e4777bc90c116accb9 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 1 Nov 2019 13:34:49 +1100 Subject: autoconf pieces for U2F support Mostly following existing logic for PKCS#11 - turning off support when either libcrypto or dlopen(3) are unavailable. --- configure.ac | 57 ++++++++++++++++++++++++++++++++++++++++++++++++--------- ssh-ecdsa-sk.c | 3 +++ ssh-keygen.c | 10 +++++++++- ssh-sk-helper.c | 11 +++++++++++ ssh-sk.c | 3 +++ 5 files changed, 74 insertions(+), 10 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/configure.ac b/configure.ac index 9b4a7ee62..8f007e635 100644 --- a/configure.ac +++ b/configure.ac @@ -1878,16 +1878,53 @@ AC_ARG_ENABLE([pkcs11], ] ) -# PKCS11 depends on OpenSSL. -if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then - # PKCS#11 support requires dlopen() and co - AC_SEARCH_LIBS([dlopen], [dl], - AC_CHECK_DECL([RTLD_NOW], - AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support]), - [], [#include ] - ) - ) +disable_sk= +AC_ARG_ENABLE([security-key], + [ --disable-security-key disable U2F/FIDO support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_sk=1 + fi + ] +) + +# PKCS11/U2F depend on OpenSSL and dlopen(). +AC_SEARCH_LIBS([dlopen], [dl]) +AC_CHECK_FUNCS([dlopen]) +AC_CHECK_DECL([RTLD_NOW], [], [], [#include ]) + +enable_pkcs11=yes +enable_sk=yes +if test "x$openssl" != "xyes" ; then + enable_pkcs11="disabled; missing libcrypto" + enable_sk="disabled; missing libcrypto" +fi +if test "x$ac_cv_func_dlopen" != "xyes" ; then + enable_pkcs11="disabled; missing dlopen(3)" + enable_sk="disabled; missing dlopen(3)" +fi +if test "x$ac_cv_have_decl_RTLD_NOW" != "xyes" ; then + enable_pkcs11="disabled; missing RTLD_NOW" + enable_sk="disabled; missing RTLD_NOW" +fi +if test ! -z "$disable_pkcs11" ; then + enable_pkcs11="disabled by user" +fi +if test ! -z "$disable_sk" ; then + enable_sk="disabled by user" +fi + +AC_MSG_CHECKING([whether to enable PKCS11]) +if test "x$enable_pkcs11" = "xyes" ; then + AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support]) +fi +AC_MSG_RESULT([$enable_pkcs11]) + +AC_MSG_CHECKING([whether to enable U2F]) +if test "x$enable_sk" = "xyes" ; then + AC_DEFINE([ENABLE_SK], [], [Enable for U2F/FIDO support]) fi +AC_MSG_RESULT([$enable_sk]) # IRIX has a const char return value for gai_strerror() AC_CHECK_FUNCS([gai_strerror], [ @@ -5247,6 +5284,8 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" echo " Random number source: $RAND_MSG" echo " Privsep sandbox style: $SANDBOX_STYLE" +echo " PKCS#11 support: $enable_pkcs11" +echo " U2F/FIDO support: $enable_sk" echo "" diff --git a/ssh-ecdsa-sk.c b/ssh-ecdsa-sk.c index 6441cd7fa..355924657 100644 --- a/ssh-ecdsa-sk.c +++ b/ssh-ecdsa-sk.c @@ -29,6 +29,8 @@ #include "includes.h" +#ifdef ENABLE_SK + #include #include @@ -178,3 +180,4 @@ ssh_ecdsa_sk_verify(const struct sshkey *key, free(ktype); return ret; } +#endif /* ENABLE_SK */ diff --git a/ssh-keygen.c b/ssh-keygen.c index 0d0586576..1d2a93f66 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -2783,7 +2783,6 @@ main(int argc, char **argv) unsigned long long ull, cert_serial = 0; char *identity_comment = NULL, *ca_key_path = NULL; u_int32_t bits = 0; - uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; FILE *f; const char *errstr; int log_level = SYSLOG_LEVEL_INFO; @@ -2796,6 +2795,9 @@ main(int argc, char **argv) unsigned long start_lineno = 0, lines_to_process = 0; BIGNUM *start = NULL; #endif +#ifdef ENABLE_SK + uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; +#endif extern int optind; extern char *optarg; @@ -2991,7 +2993,9 @@ main(int argc, char **argv) "number", optarg); if (ull > 0xff) fatal("Invalid security key flags 0x%llx", ull); +#ifdef ENABLE_SK sk_flags = (uint8_t)ull; +#endif break; case 'z': errno = 0; @@ -3250,10 +3254,14 @@ main(int argc, char **argv) printf("Generating public/private %s key pair.\n", key_type_name); if (type == KEY_ECDSA_SK) { +#ifndef ENABLE_SK + fatal("Security key support was disabled at compile time"); +#else /* ENABLE_SK */ if (sshsk_enroll(sk_provider, cert_key_id == NULL ? "ssh:" : cert_key_id, sk_flags, NULL, &private, NULL) != 0) exit(1); /* error message already printed */ +#endif /* ENABLE_SK */ } else if ((r = sshkey_generate(type, bits, &private)) != 0) fatal("sshkey_generate failed"); if ((r = sshkey_from_private(private, &public)) != 0) diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 0a0c92a44..ced00d955 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -51,6 +51,7 @@ #include "ssherr.h" #include "ssh-sk.h" +#ifdef ENABLE_SK extern char *__progname; int @@ -141,3 +142,13 @@ main(int argc, char **argv) return (0); } +#else /* ENABLE_SK */ +#include + +int +main(int argc, char **argv) +{ + fprintf(stderr, "ssh-sk-helper: disabled at compile time\n"); + return -1; +} +#endif /* ENABLE_SK */ diff --git a/ssh-sk.c b/ssh-sk.c index 7d313f57b..122a1e2b7 100644 --- a/ssh-sk.c +++ b/ssh-sk.c @@ -19,6 +19,8 @@ #include "includes.h" +#ifdef ENABLE_SK + #include #include #include @@ -375,3 +377,4 @@ sshsk_ecdsa_sign(const char *provider_path, const struct sshkey *key, sshbuf_free(inner_sig); return r; } +#endif /* ENABLE_SK */ -- cgit v1.2.3 From e03a29e6554cd0c9cdbac0dae53dd79e6eb4ea47 Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Tue, 12 Nov 2019 19:30:50 +0000 Subject: upstream: rename sshsk_ecdsa_sign() to sshsk_sign(); ok djm OpenBSD-Commit-ID: 1524042e09d81e54c4470d7bfcc0194c5b46fe19 --- ssh-sk-helper.c | 4 ++-- ssh-sk.c | 4 ++-- ssh-sk.h | 8 ++++---- sshkey.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index ced00d955..a996f5898 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.1 2019/10/31 21:22:01 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.2 2019/11/12 19:30:50 markus Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -128,7 +128,7 @@ main(int argc, char **argv) "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), provider, msglen, (u_long)compat); - if ((r = sshsk_ecdsa_sign(provider, key, &sig, &siglen, + if ((r = sshsk_sign(provider, key, &sig, &siglen, message, msglen, compat)) != 0) fatal("Signing failed: %s", ssh_err(r)); diff --git a/ssh-sk.c b/ssh-sk.c index 0f2e311d0..e11fd1912 100644 --- a/ssh-sk.c +++ b/ssh-sk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.c,v 1.3 2019/11/12 19:30:21 markus Exp $ */ +/* $OpenBSD: ssh-sk.c,v 1.4 2019/11/12 19:30:50 markus Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -342,7 +342,7 @@ out: } int -sshsk_ecdsa_sign(const char *provider_path, const struct sshkey *key, +sshsk_sign(const char *provider_path, const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat) { diff --git a/ssh-sk.h b/ssh-sk.h index 5033e6f68..7800609c4 100644 --- a/ssh-sk.h +++ b/ssh-sk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.h,v 1.2 2019/10/31 21:22:01 djm Exp $ */ +/* $OpenBSD: ssh-sk.h,v 1.3 2019/11/12 19:30:50 markus Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -39,12 +39,12 @@ int sshsk_enroll(const char *provider_path, const char *application, struct sshbuf *attest); /* - * Calculate an ECDSA_SK signature using the specified key and provider - * middleware. + * Calculate an ECDSA_SK signature using the specified key + * and provider middleware. * * Returns 0 on success or a ssherr.h error code on failure. */ -int sshsk_ecdsa_sign(const char *provider_path, const struct sshkey *key, +int sshsk_sign(const char *provider_path, const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); diff --git a/sshkey.c b/sshkey.c index b72f38a2f..ed8f4ef00 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.87 2019/11/12 19:29:24 markus Exp $ */ +/* $OpenBSD: sshkey.c,v 1.88 2019/11/12 19:30:50 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -2687,8 +2687,8 @@ sshkey_sign(struct sshkey *key, # ifdef ENABLE_SK case KEY_ECDSA_SK_CERT: case KEY_ECDSA_SK: - r = sshsk_ecdsa_sign(sk_provider, key, sigp, lenp, - data, datalen, compat); + r = sshsk_sign(sk_provider, key, sigp, lenp, data, datalen, + compat); break; # endif /* ENABLE_SK */ # endif /* OPENSSL_HAS_ECC */ -- cgit v1.2.3 From 2c55744a56de0ffc81fe445a1e7fc5cd308712b3 Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Tue, 12 Nov 2019 19:33:08 +0000 Subject: upstream: enable ed25519 support; ok djm OpenBSD-Commit-ID: 1a399c5b3ef15bd8efb916110cf5a9e0b554ab7e --- authfd.c | 4 +- myproposal.h | 4 +- pathnames.h | 3 +- readconf.c | 4 +- ssh-add.c | 5 +- ssh-agent.c | 4 +- ssh-keygen.c | 25 ++++++--- ssh-sk-helper.c | 4 +- sshconnect.c | 4 +- sshconnect2.c | 13 ++--- sshkey.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- sshkey.h | 7 +-- 12 files changed, 200 insertions(+), 31 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/authfd.c b/authfd.c index 1f0cd2ab3..e014f765c 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.118 2019/10/31 21:19:14 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.119 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -484,6 +484,8 @@ ssh_add_identity_constrained(int sock, struct sshkey *key, #endif case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: case KEY_XMSS: case KEY_XMSS_CERT: type = constrained ? diff --git a/myproposal.h b/myproposal.h index a22649a2e..90bb67bb3 100644 --- a/myproposal.h +++ b/myproposal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: myproposal.h,v 1.60 2019/11/01 02:32:05 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.61 2019/11/12 19:33:08 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -149,6 +149,7 @@ "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ + "sk-ssh-ed25519-cert-v01@openssh.com," \ "ssh-ed25519-cert-v01@openssh.com," \ "rsa-sha2-512-cert-v01@openssh.com," \ "rsa-sha2-256-cert-v01@openssh.com," \ @@ -157,6 +158,7 @@ "ecdsa-sha2-nistp256," \ "ecdsa-sha2-nistp384," \ "ecdsa-sha2-nistp521," \ + "sk-ssh-ed25519@openssh.com," \ "ssh-ed25519," \ "rsa-sha2-512," \ "rsa-sha2-256," \ diff --git a/pathnames.h b/pathnames.h index 3a1bd1977..f7ca5a75a 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.30 2019/10/31 21:22:01 djm Exp $ */ +/* $OpenBSD: pathnames.h,v 1.31 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen @@ -78,6 +78,7 @@ #define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" #define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss" #define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk" +#define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk" /* * Configuration file in user's home directory. This file need not be diff --git a/readconf.c b/readconf.c index f18194580..3d99367c3 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.310 2019/10/31 21:18:28 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.311 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2055,6 +2055,8 @@ fill_default_options(Options * options) #endif add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ED25519, 0); + add_identity_file(options, "~/", + _PATH_SSH_CLIENT_ID_ED25519_SK, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0); } if (options->escape_char == -1) diff --git a/ssh-add.c b/ssh-add.c index 3c8849ac4..696b156d5 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.143 2019/10/31 21:19:56 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.144 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -81,6 +81,7 @@ static char *default_files[] = { #endif #endif /* WITH_OPENSSL */ _PATH_SSH_CLIENT_ID_ED25519, + _PATH_SSH_CLIENT_ID_ED25519_SK, _PATH_SSH_CLIENT_ID_XMSS, NULL }; @@ -312,7 +313,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, ssh_free_identitylist(idlist); } - if (sshkey_type_plain(private->type) != KEY_ECDSA_SK) + if (!sshkey_is_sk(private)) skprovider = NULL; /* Don't send constraint for other keys */ else if (skprovider == NULL) { fprintf(stderr, "Cannot load security key %s without " diff --git a/ssh-agent.c b/ssh-agent.c index 07f19c53a..dd9f85ae7 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.239 2019/10/31 21:23:19 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.240 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -622,7 +622,7 @@ process_add_identity(SocketEntry *e) } } if (sk_provider != NULL) { - if (sshkey_type_plain(k->type) != KEY_ECDSA_SK) { + if (!sshkey_is_sk(k)) { error("Cannot add provider: %s is not a security key", sshkey_type(k)); free(sk_provider); diff --git a/ssh-keygen.c b/ssh-keygen.c index ac34f314b..030b3684e 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.361 2019/11/08 03:54:02 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.362 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -287,6 +287,10 @@ ask_filename(struct passwd *pw, const char *prompt) case KEY_ED25519_CERT: name = _PATH_SSH_CLIENT_ID_ED25519; break; + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + name = _PATH_SSH_CLIENT_ID_ED25519_SK; + break; case KEY_XMSS: case KEY_XMSS_CERT: name = _PATH_SSH_CLIENT_ID_XMSS; @@ -3255,16 +3259,23 @@ main(int argc, char **argv) printf("Generating public/private %s key pair.\n", key_type_name); if (type == KEY_ECDSA_SK) { + switch (type) { + case KEY_ECDSA_SK: + case KEY_ED25519_SK: #ifndef ENABLE_SK fatal("Security key support was disabled at compile time"); #else /* ENABLE_SK */ - if (sshsk_enroll(sk_provider, - cert_key_id == NULL ? "ssh:" : cert_key_id, - sk_flags, NULL, &private, NULL) != 0) - exit(1); /* error message already printed */ + if (sshsk_enroll(type, sk_provider, + cert_key_id == NULL ? "ssh:" : cert_key_id, + sk_flags, NULL, &private, NULL) != 0) + exit(1); /* error message already printed */ + break; #endif /* ENABLE_SK */ - } else if ((r = sshkey_generate(type, bits, &private)) != 0) - fatal("sshkey_generate failed"); + default: + if ((r = sshkey_generate(type, bits, &private)) != 0) + fatal("sshkey_generate failed"); + break; + } if ((r = sshkey_from_private(private, &public)) != 0) fatal("sshkey_from_private failed: %s\n", ssh_err(r)); diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index a996f5898..0acb8d172 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.2 2019/11/12 19:30:50 markus Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.3 2019/11/12 19:33:08 markus Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -114,7 +114,7 @@ main(int argc, char **argv) if ((r = sshbuf_froms(req, &kbuf)) != 0 || (r = sshkey_private_deserialize(kbuf, &key)) != 0) fatal("Unable to parse key: %s", ssh_err(r)); - if (sshkey_type_plain(key->type) != KEY_ECDSA_SK) + if (!sshkey_is_sk(key)) fatal("Unsupported key type %s", sshkey_ssh_name(key)); if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || diff --git a/sshconnect.c b/sshconnect.c index 177775f6e..7e9369ee3 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.321 2019/10/31 21:20:38 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.322 2019/11/12 19:33:08 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1425,7 +1425,7 @@ maybe_add_key_to_agent(char *authfile, struct sshkey *private, close(auth_sock); return; } - if (sshkey_type_plain(private->type) == KEY_ECDSA_SK) + if (sshkey_is_sk(private)) skprovider = options.sk_provider; if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, (options.add_keys_to_agent == 3), 0, skprovider)) == 0) diff --git a/sshconnect2.c b/sshconnect2.c index 867d463d6..4e5cddf14 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.310 2019/10/31 21:23:19 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.311 2019/11/12 19:33:08 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -611,7 +611,7 @@ format_identity(Identity *id) if (id->key) { if ((id->key->flags & SSHKEY_FLAG_EXT) != 0) note = " token"; - else if (sshkey_type_plain(id->key->type) == KEY_ECDSA_SK) + else if (sshkey_is_sk(id->key)) note = " security-key"; } xasprintf(&ret, "%s %s%s%s%s%s%s", @@ -1468,8 +1468,7 @@ load_identity_file(Identity *id) quit = 1; break; } - if (private != NULL && - sshkey_type_plain(private->type) == KEY_ECDSA_SK && + if (private != NULL && sshkey_is_sk(private) && options.sk_provider == NULL) { debug("key \"%s\" is a security key, but no " "provider specified", id->filename); @@ -1554,8 +1553,7 @@ pubkey_prepare(Authctxt *authctxt) options.identity_files[i]); continue; } - if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK && - options.sk_provider == NULL) { + if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { debug("%s: ignoring security key %s as no " "SecurityKeyProvider has been specified", __func__, options.identity_files[i]); @@ -1579,8 +1577,7 @@ pubkey_prepare(Authctxt *authctxt) options.identity_files[i]); continue; } - if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK && - options.sk_provider == NULL) { + if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { debug("%s: ignoring security key certificate %s as no " "SecurityKeyProvider has been specified", __func__, options.identity_files[i]); diff --git a/sshkey.c b/sshkey.c index 269f37b39..1b66d4ec7 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.89 2019/11/12 19:31:18 markus Exp $ */ +/* $OpenBSD: sshkey.c,v 1.90 2019/11/12 19:33:08 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -107,6 +107,10 @@ static const struct keytype keytypes[] = { { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 }, { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL, KEY_ED25519_CERT, 0, 1, 0 }, + { "sk-ssh-ed25519@openssh.com", "ED25519-SK", NULL, + KEY_ED25519_SK, 0, 0, 0 }, + { "sk-ssh-ed25519-cert-v01@openssh.com", "ED25519-SK-CERT", NULL, + KEY_ED25519_SK_CERT, 0, 1, 0 }, #ifdef WITH_XMSS { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 }, { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL, @@ -337,6 +341,8 @@ sshkey_size(const struct sshkey *k) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: case KEY_XMSS: case KEY_XMSS_CERT: return 256; /* XXX */ @@ -353,6 +359,7 @@ sshkey_type_is_valid_ca(int type) case KEY_ECDSA: case KEY_ECDSA_SK: case KEY_ED25519: + case KEY_ED25519_SK: case KEY_XMSS: return 1; default: @@ -368,6 +375,20 @@ sshkey_is_cert(const struct sshkey *k) return sshkey_type_is_cert(k->type); } +int +sshkey_is_sk(const struct sshkey *k) +{ + if (k == NULL) + return 0; + switch (sshkey_type_plain(k->type)) { + case KEY_ECDSA_SK: + case KEY_ED25519_SK: + return 1; + default: + return 0; + } +} + /* Return the cert-less equivalent to a certified key type */ int sshkey_type_plain(int type) @@ -383,6 +404,8 @@ sshkey_type_plain(int type) return KEY_ECDSA_SK; case KEY_ED25519_CERT: return KEY_ED25519; + case KEY_ED25519_SK_CERT: + return KEY_ED25519_SK; case KEY_XMSS_CERT: return KEY_XMSS; default: @@ -563,6 +586,8 @@ sshkey_new(int type) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: case KEY_XMSS: case KEY_XMSS_CERT: /* no need to prealloc */ @@ -615,6 +640,12 @@ sshkey_free(struct sshkey *k) break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + free(k->sk_application); + sshbuf_free(k->sk_key_handle); + sshbuf_free(k->sk_reserved); + /* FALLTHROUGH */ case KEY_ED25519: case KEY_ED25519_CERT: freezero(k->ed25519_pk, ED25519_PK_SZ); @@ -734,6 +765,13 @@ sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) return 1; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + if (a->sk_application == NULL || b->sk_application == NULL) + return 0; + if (strcmp(a->sk_application, b->sk_application) != 0) + return 0; + /* FALLTHROUGH */ case KEY_ED25519: case KEY_ED25519_CERT: return a->ed25519_pk != NULL && b->ed25519_pk != NULL && @@ -842,12 +880,18 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, break; #endif /* WITH_OPENSSL */ case KEY_ED25519: + case KEY_ED25519_SK: if (key->ed25519_pk == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || (ret = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0) return ret; + if (type == KEY_ED25519_SK) { + if ((ret = sshbuf_put_cstring(b, + key->sk_application)) != 0) + return ret; + } break; #ifdef WITH_XMSS case KEY_XMSS: @@ -1290,11 +1334,13 @@ sshkey_read(struct sshkey *ret, char **cpp) case KEY_ECDSA: case KEY_ECDSA_SK: case KEY_ED25519: + case KEY_ED25519_SK: case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ECDSA_SK_CERT: case KEY_RSA_CERT: case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: #ifdef WITH_XMSS case KEY_XMSS: case KEY_XMSS_CERT: @@ -1418,6 +1464,13 @@ sshkey_read(struct sshkey *ret, char **cpp) /* XXX */ #endif break; + case KEY_ED25519_SK: + freezero(ret->ed25519_pk, ED25519_PK_SZ); + ret->ed25519_pk = k->ed25519_pk; + ret->sk_application = k->sk_application; + k->ed25519_pk = NULL; + k->sk_application = NULL; + break; #ifdef WITH_XMSS case KEY_XMSS: free(ret->xmss_pk); @@ -1876,6 +1929,8 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: if (k->ed25519_pk != NULL) { if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { r = SSH_ERR_ALLOC_FAIL; @@ -1883,6 +1938,12 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) } memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); } + if (k->type != KEY_ED25519_SK && + k->type != KEY_ED25519_SK_CERT) + break; + /* Append security-key application string */ + if ((n->sk_application = strdup(k->sk_application)) == NULL) + goto out; break; #ifdef WITH_XMSS case KEY_XMSS: @@ -2444,6 +2505,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; @@ -2451,6 +2513,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, } /* FALLTHROUGH */ case KEY_ED25519: + case KEY_ED25519_SK: if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) goto out; if (len != ED25519_PK_SZ) { @@ -2461,6 +2524,17 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, ret = SSH_ERR_ALLOC_FAIL; goto out; } + if (type == KEY_ED25519_SK || type == KEY_ED25519_SK_CERT) { + /* Parse additional security-key application string */ + if (sshbuf_get_cstring(b, &key->sk_application, + NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "App: %s\n", key->sk_application); +#endif + } key->ed25519_pk = pk; pk = NULL; break; @@ -2790,6 +2864,9 @@ sshkey_to_certified(struct sshkey *k) newtype = KEY_ECDSA_SK_CERT; break; #endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + newtype = KEY_ED25519_SK_CERT; + break; case KEY_ED25519: newtype = KEY_ED25519_CERT; break; @@ -3223,6 +3300,29 @@ sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, ED25519_SK_SZ)) != 0) goto out; break; + case KEY_ED25519_SK: + if ((r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; + case KEY_ED25519_SK_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; #ifdef WITH_XMSS case KEY_XMSS: if (key->xmss_name == NULL) { @@ -3532,6 +3632,57 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) k->ed25519_sk = ed25519_sk; ed25519_pk = ed25519_sk = NULL; /* transferred */ break; + case KEY_ED25519_SK: + if ((k = sshkey_new(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) + goto out; + if (pklen != ED25519_PK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + k->ed25519_pk = ed25519_pk; + ed25519_pk = NULL; + break; + case KEY_ED25519_SK_CERT: + if ((r = sshkey_froms(buf, &k)) != 0 || + (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) + goto out; + if (k->type != type) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (pklen != ED25519_PK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + k->ed25519_pk = ed25519_pk; + ed25519_pk = NULL; /* transferred */ + break; #ifdef WITH_XMSS case KEY_XMSS: if ((k = sshkey_new(type)) == NULL) { @@ -4261,6 +4412,7 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, break; /* see below */ #endif /* WITH_OPENSSL */ case KEY_ED25519: + case KEY_ED25519_SK: #ifdef WITH_XMSS case KEY_XMSS: #endif /* WITH_XMSS */ diff --git a/sshkey.h b/sshkey.h index 8cd12bd95..1fb8369f0 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.37 2019/11/12 19:29:25 markus Exp $ */ +/* $OpenBSD: sshkey.h,v 1.38 2019/11/12 19:33:08 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -129,7 +129,7 @@ struct sshkey { /* KEY_ECDSA and KEY_ECDSA_SK */ int ecdsa_nid; /* NID of curve */ EC_KEY *ecdsa; - /* KEY_ED25519 */ + /* KEY_ED25519 and KEY_ED25519_SK */ u_char *ed25519_sk; u_char *ed25519_pk; /* KEY_XMSS */ @@ -138,7 +138,7 @@ struct sshkey { void *xmss_state; /* depends on xmss_name, opaque */ u_char *xmss_sk; u_char *xmss_pk; - /* KEY_ECDSA_SK */ + /* KEY_ECDSA_SK and KEY_ED25519_SK */ char *sk_application; uint8_t sk_flags; struct sshbuf *sk_key_handle; @@ -180,6 +180,7 @@ int sshkey_unshield_private(struct sshkey *); int sshkey_type_from_name(const char *); int sshkey_is_cert(const struct sshkey *); +int sshkey_is_sk(const struct sshkey *); int sshkey_type_is_cert(int); int sshkey_type_plain(int); int sshkey_to_certified(struct sshkey *); -- cgit v1.2.3 From 611073fb40ecaf4ac65094e403edea3a08deb700 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Fri, 13 Dec 2019 19:11:14 +0000 Subject: upstream: perform security key enrollment via ssh-sk-helper too. This means that ssh-keygen no longer needs to link against ssh-sk-helper, and only ssh-sk-helper needs libfido2 and /dev/uhid* access; feedback & ok markus@ OpenBSD-Commit-ID: 9464233fab95708d2ff059f8bee29c0d1f270800 --- ssh-sk-helper.c | 160 ++++++++++++++++++++++++++++++++++++++++++-------------- sshkey.h | 8 ++- 2 files changed, 127 insertions(+), 41 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 0acb8d172..eade26e3d 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.3 2019/11/12 19:33:08 markus Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.4 2019/12/13 19:11:14 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -18,20 +18,16 @@ /* * This is a tiny program used to isolate the address space used for * security key middleware signing operations from ssh-agent. It is similar - * to ssh-pkcs11-helper.c but considerably simpler as the signing operation - * for this case are stateless. + * to ssh-pkcs11-helper.c but considerably simpler as the operations for + * security keys are stateless. * - * It receives a signing request (key, provider, message, flags) from - * stdin, attempts to perform a signature using the security key provider - * and returns the resultant signature via stdout. - * - * In the future, this program might gain additional functions to support - * FIDO2 tokens such as enumerating resident keys. When this happens it will - * be necessary to crank SSH_SK_HELPER_VERSION below. + * Please crank SSH_SK_HELPER_VERSION in sshkey.h for any incompatible + * protocol changes. */ - + #include "includes.h" +#include #include #include #include @@ -54,19 +50,112 @@ #ifdef ENABLE_SK extern char *__progname; -int -main(int argc, char **argv) +static struct sshbuf * +process_sign(struct sshbuf *req) { - SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; - LogLevel log_level = SYSLOG_LEVEL_ERROR; - struct sshbuf *req, *resp, *kbuf; + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *resp, *kbuf; struct sshkey *key; uint32_t compat; const u_char *message; - u_char version, *sig; + u_char *sig; size_t msglen, siglen; char *provider; + + if ((r = sshbuf_froms(req, &kbuf)) != 0 || + (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || + (r = sshbuf_get_cstring(req, NULL, NULL)) != 0 || /* alg */ + (r = sshbuf_get_u32(req, &compat)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) + fatal("Unable to parse private key: %s", ssh_err(r)); + if (!sshkey_is_sk(key)) + fatal("Unsupported key type %s", sshkey_ssh_name(key)); + + debug("%s: ready to sign with key %s, provider %s: " + "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), + provider, msglen, (u_long)compat); + + if ((r = sshsk_sign(provider, key, &sig, &siglen, + message, msglen, compat)) != 0) + fatal("Signing failed: %s", ssh_err(r)); + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + + sshbuf_free(kbuf); + free(provider); + + return resp; +} + +static struct sshbuf * +process_enroll(struct sshbuf *req) +{ + int r; + u_int type; + char *provider; + char *application; + uint8_t flags; + struct sshbuf *challenge, *attest, *kbuf, *resp; + struct sshkey *key; + + if ((resp = sshbuf_new()) == NULL || + (attest = sshbuf_new()) == NULL || + (kbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_get_u32(req, &type)) != 0 || + (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || + (r = sshbuf_get_u8(req, &flags)) != 0 || + (r = sshbuf_froms(req, &challenge)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + if (type > INT_MAX) + fatal("%s: bad type %u", __progname, type); + if (sshbuf_len(challenge) == 0) { + sshbuf_free(challenge); + challenge = NULL; + } + + if ((r = sshsk_enroll((int)type, provider, application, flags, + challenge, &key, attest)) != 0) + fatal("%s: sshsk_enroll failed: %s", __progname, ssh_err(r)); + + if ((r = sshkey_private_serialize(key, kbuf)) != 0) + fatal("%s: serialize private key: %s", __progname, ssh_err(r)); + if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 || + (r = sshbuf_put_stringb(resp, attest)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + + sshkey_free(key); + sshbuf_free(kbuf); + sshbuf_free(attest); + sshbuf_free(challenge); + free(provider); + free(application); + + return resp; +} + +int +main(int argc, char **argv) +{ + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; int in, out, ch, r, log_stderr = 0; + u_int rtype; + uint8_t version; sanitise_stdfd(); log_init(__progname, log_level, log_facility, log_stderr); @@ -98,7 +187,7 @@ main(int argc, char **argv) close(STDOUT_FILENO); sanitise_stdfd(); /* resets to /dev/null */ - if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) + if ((req = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); if (ssh_msg_recv(in, req) < 0) fatal("ssh_msg_recv failed"); @@ -111,33 +200,26 @@ main(int argc, char **argv) fatal("unsupported version: received %d, expected %d", version, SSH_SK_HELPER_VERSION); } - if ((r = sshbuf_froms(req, &kbuf)) != 0 || - (r = sshkey_private_deserialize(kbuf, &key)) != 0) - fatal("Unable to parse key: %s", ssh_err(r)); - if (!sshkey_is_sk(key)) - fatal("Unsupported key type %s", sshkey_ssh_name(key)); - if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || - (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || - (r = sshbuf_get_u32(req, &compat)) != 0) + if ((r = sshbuf_get_u32(req, &rtype)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); - if (sshbuf_len(req) != 0) - fatal("%s: trailing data in request", __progname); - - debug("%s: ready to sign with key %s, provider %s: " - "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), - provider, msglen, (u_long)compat); - - if ((r = sshsk_sign(provider, key, &sig, &siglen, - message, msglen, compat)) != 0) - fatal("Signing failed: %s", ssh_err(r)); - /* send reply */ - if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) - fatal("%s: buffer error: %s", __progname, ssh_err(r)); + switch (rtype) { + case SSH_SK_HELPER_SIGN: + resp = process_sign(req); + break; + case SSH_SK_HELPER_ENROLL: + resp = process_enroll(req); + break; + default: + fatal("%s: unsupported request type %u", __progname, rtype); + } + sshbuf_free(req); debug("%s: reply len %zu", __progname, sshbuf_len(resp)); + if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1) fatal("ssh_msg_send failed"); + sshbuf_free(resp); close(out); return (0); diff --git a/sshkey.h b/sshkey.h index d96dcb8b0..21ac802dc 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.41 2019/12/13 19:09:10 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.42 2019/12/13 19:11:14 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -52,7 +52,11 @@ #define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20) /* Version of protocol expected from ssh-sk-helper */ -#define SSH_SK_HELPER_VERSION 1 +#define SSH_SK_HELPER_VERSION 2 + +/* ssh-sk-helper messages */ +#define SSH_SK_HELPER_SIGN 1 +#define SSH_SK_HELPER_ENROLL 2 struct sshbuf; -- cgit v1.2.3 From f45f3a8a12e2bee601046b916e6c5cd6eae08048 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sat, 14 Dec 2019 07:53:11 +1100 Subject: fixup --- ssh-sk-helper.c | 1 + 1 file changed, 1 insertion(+) (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index eade26e3d..3dc149b95 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -153,6 +153,7 @@ main(int argc, char **argv) { SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; LogLevel log_level = SYSLOG_LEVEL_ERROR; + struct sshbuf *req, *resp; int in, out, ch, r, log_stderr = 0; u_int rtype; uint8_t version; -- cgit v1.2.3 From 27753a8e21887d47fe6b5c78a4aed0efe558a850 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 30 Dec 2019 09:21:59 +0000 Subject: upstream: implement loading of resident keys in ssh-sk-helper feedback and ok markus@ OpenBSD-Commit-ID: b273c23769ea182c55c4a7b8f9cbd9181722011a --- ssh-sk-client.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- ssh-sk-helper.c | 49 ++++++++++++++++++++++++++++++++++++++- sshkey.h | 3 ++- 3 files changed, 121 insertions(+), 3 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-client.c b/ssh-sk-client.c index 8a7ac97c4..b2f062455 100644 --- a/ssh-sk-client.c +++ b/ssh-sk-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-client.c,v 1.1 2019/12/13 20:16:56 djm Exp $ */ +/* $OpenBSD: ssh-sk-client.c,v 1.2 2019/12/30 09:21:59 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -331,3 +331,73 @@ sshsk_enroll(int type, const char *provider_path, const char *application, errno = oerrno; return r; } + +int +sshsk_load_resident(const char *provider_path, const char *pin, + struct sshkey ***keysp, size_t *nkeysp) +{ + int oerrno, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; + struct sshkey *key = NULL, **keys = NULL, **tmp; + size_t i, nkeys = 0; + + *keysp = NULL; + *nkeysp = 0; + + if ((resp = sshbuf_new()) == NULL || + (kbuf = sshbuf_new()) == NULL || + (req = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 || + (r = sshbuf_put_cstring(req, provider_path)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0) { + error("%s: compose: %s", __func__, ssh_err(r)); + goto out; + } + + if ((r = client_converse(req, &resp)) != 0) + goto out; + + while (sshbuf_len(resp) != 0) { + /* key, comment */ + if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || + (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0) { + error("%s: parse signature: %s", __func__, ssh_err(r)); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { + error("Unable to parse private key: %s", ssh_err(r)); + goto out; + } + if ((tmp = recallocarray(keys, nkeys, nkeys + 1, + sizeof(*keys))) == NULL) { + error("%s: recallocarray keys failed", __func__); + goto out; + } + keys = tmp; + keys[nkeys++] = key; + key = NULL; + } + + /* success */ + r = 0; + *keysp = keys; + *nkeysp = nkeys; + keys = NULL; + nkeys = 0; + out: + oerrno = errno; + for (i = 0; i < nkeys; i++) + sshkey_free(keys[i]); + free(keys); + sshkey_free(key); + sshbuf_free(kbuf); + sshbuf_free(req); + sshbuf_free(resp); + errno = oerrno; + return r; +} diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 3dc149b95..ac528cfcf 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.4 2019/12/13 19:11:14 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.5 2019/12/30 09:21:59 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -148,6 +148,50 @@ process_enroll(struct sshbuf *req) return resp; } +static struct sshbuf * +process_load_resident(struct sshbuf *req) +{ + int r; + char *provider, *pin; + struct sshbuf *kbuf, *resp; + struct sshkey **keys = NULL; + size_t nkeys = 0, i; + + if ((resp = sshbuf_new()) == NULL || + (kbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) + fatal("%s: sshsk_load_resident failed: %s", + __progname, ssh_err(r)); + + for (i = 0; i < nkeys; i++) { + debug("%s: key %zu %s %s", __func__, i, + sshkey_type(keys[i]), keys[i]->sk_application); + sshbuf_reset(kbuf); + if ((r = sshkey_private_serialize(keys[i], kbuf)) != 0) + fatal("%s: serialize private key: %s", + __progname, ssh_err(r)); + if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 || + (r = sshbuf_put_cstring(resp, "")) != 0) /* comment */ + fatal("%s: buffer error: %s", __progname, ssh_err(r)); + } + + for (i = 0; i < nkeys; i++) + sshkey_free(keys[i]); + free(keys); + sshbuf_free(kbuf); + free(provider); + freezero(pin, strlen(pin)); + return resp; +} + int main(int argc, char **argv) { @@ -212,6 +256,9 @@ main(int argc, char **argv) case SSH_SK_HELPER_ENROLL: resp = process_enroll(req); break; + case SSH_SK_HELPER_LOAD_RESIDENT: + resp = process_load_resident(req); + break; default: fatal("%s: unsupported request type %u", __progname, rtype); } diff --git a/sshkey.h b/sshkey.h index 21ac802dc..699c6b817 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.42 2019/12/13 19:11:14 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.43 2019/12/30 09:21:59 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -57,6 +57,7 @@ /* ssh-sk-helper messages */ #define SSH_SK_HELPER_SIGN 1 #define SSH_SK_HELPER_ENROLL 2 +#define SSH_SK_HELPER_LOAD_RESIDENT 3 struct sshbuf; -- cgit v1.2.3 From c54cd1892c3e7f268b21e1f07ada9f0d9816ffc0 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 30 Dec 2019 09:23:28 +0000 Subject: upstream: SK API and sk-helper error/PIN passing Allow passing a PIN via the SK API (API major crank) and let the ssh-sk-helper API follow. Also enhance the ssh-sk-helper API to support passing back an error code instead of a complete reply. Will be used to signal "wrong PIN", etc. feedback and ok markus@ OpenBSD-Commit-ID: a1bd6b0a2421646919a0c139b8183ad76d28fb71 --- sk-api.h | 8 ++--- sk-usbhid.c | 10 +++--- ssh-keygen.c | 4 +-- ssh-sk-client.c | 47 +++++++++++++++++++------ ssh-sk-helper.c | 106 ++++++++++++++++++++++++++++++++++++++++++++------------ ssh-sk.c | 31 +++++++++-------- ssh-sk.h | 17 ++++++--- sshkey.c | 4 +-- sshkey.h | 10 +----- 9 files changed, 165 insertions(+), 72 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/sk-api.h b/sk-api.h index 10f1fdb10..4f9f43ee6 100644 --- a/sk-api.h +++ b/sk-api.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sk-api.h,v 1.4 2019/12/30 09:21:16 djm Exp $ */ +/* $OpenBSD: sk-api.h,v 1.5 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -59,7 +59,7 @@ struct sk_resident_key { struct sk_enroll_response key; }; -#define SSH_SK_VERSION_MAJOR 0x00020000 /* current API version */ +#define SSH_SK_VERSION_MAJOR 0x00030000 /* current API version */ #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 /* Return the version of the middleware API */ @@ -67,13 +67,13 @@ uint32_t sk_api_version(void); /* Enroll a U2F key (private key generation) */ int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, - const char *application, uint8_t flags, + const char *application, uint8_t flags, const char *pin, struct sk_enroll_response **enroll_response); /* Sign a challenge */ int sk_sign(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, struct sk_sign_response **sign_response); /* Enumerate all resident keys */ int sk_load_resident_keys(const char *pin, diff --git a/sk-usbhid.c b/sk-usbhid.c index fa4424483..54ce0bddf 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c @@ -54,7 +54,7 @@ } while (0) #endif -#define SK_VERSION_MAJOR 0x00020000 /* current API version */ +#define SK_VERSION_MAJOR 0x00030000 /* current API version */ /* Flags */ #define SK_USER_PRESENCE_REQD 0x01 @@ -105,13 +105,13 @@ uint32_t sk_api_version(void); /* Enroll a U2F key (private key generation) */ int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, - const char *application, uint8_t flags, + const char *application, uint8_t flags, const char *pin, struct sk_enroll_response **enroll_response); /* Sign a challenge */ int sk_sign(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, struct sk_sign_response **sign_response); /* Load resident keys */ int sk_load_resident_keys(const char *pin, @@ -414,7 +414,7 @@ pack_public_key(int alg, const fido_cred_t *cred, int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, - const char *application, uint8_t flags, + const char *application, uint8_t flags, const char *pin, struct sk_enroll_response **enroll_response) { fido_cred_t *cred = NULL; @@ -652,7 +652,7 @@ int sk_sign(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, struct sk_sign_response **sign_response) + uint8_t flags, const char *pin, struct sk_sign_response **sign_response) { fido_assert_t *assert = NULL; fido_dev_t *dev = NULL; diff --git a/ssh-keygen.c b/ssh-keygen.c index 48342c09d..79e2e92b5 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.377 2019/12/30 09:19:52 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.378 2019/12/30 09:23:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -3368,7 +3368,7 @@ main(int argc, char **argv) fflush(stdout); if (sshsk_enroll(type, sk_provider, cert_key_id == NULL ? "ssh:" : cert_key_id, - sk_flags, NULL, &private, NULL) != 0) + sk_flags, NULL, NULL, &private, NULL) != 0) exit(1); /* error message already printed */ break; default: diff --git a/ssh-sk-client.c b/ssh-sk-client.c index b2f062455..0033a6655 100644 --- a/ssh-sk-client.c +++ b/ssh-sk-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-client.c,v 1.2 2019/12/30 09:21:59 djm Exp $ */ +/* $OpenBSD: ssh-sk-client.c,v 1.3 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -128,14 +129,14 @@ reap_helper(pid_t pid) } static int -client_converse(struct sshbuf *req, struct sshbuf **respp) +client_converse(struct sshbuf *req, struct sshbuf **respp, u_int msg) { int oerrno, fd, r2, r = SSH_ERR_INTERNAL_ERROR; + u_int rmsg, rerr; pid_t pid; u_char version; void (*osigchld)(int); struct sshbuf *resp = NULL; - *respp = NULL; if ((r = start_helper(&fd, &pid, &osigchld)) != 0) @@ -164,6 +165,28 @@ client_converse(struct sshbuf *req, struct sshbuf **respp) r = SSH_ERR_INVALID_FORMAT; goto out; } + if ((r = sshbuf_get_u32(resp, &rmsg)) != 0) { + error("%s: parse message type: %s", __func__, ssh_err(r)); + goto out; + } + if (rmsg == SSH_SK_HELPER_ERROR) { + if ((r = sshbuf_get_u32(resp, &rerr)) != 0) { + error("%s: parse error: %s", __func__, ssh_err(r)); + goto out; + } + debug("%s: helper returned error -%u", __func__, rerr); + /* OpenSSH error values are negative; encoded as -err on wire */ + if (rerr == 0 || rerr >= INT_MAX) + r = SSH_ERR_INTERNAL_ERROR; + else + r = -(int)rerr; + goto out; + } else if (rmsg != msg) { + error("%s: helper returned incorrect message type %u, " + "expecting %u", __func__, rmsg, msg); + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } /* success */ r = 0; out: @@ -189,7 +212,7 @@ client_converse(struct sshbuf *req, struct sshbuf **respp) int sshsk_sign(const char *provider, struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, - u_int compat) + u_int compat, const char *pin) { int oerrno, r = SSH_ERR_INTERNAL_ERROR; char *fp = NULL; @@ -217,7 +240,8 @@ sshsk_sign(const char *provider, struct sshkey *key, (r = sshbuf_put_cstring(req, provider)) != 0 || (r = sshbuf_put_string(req, data, datalen)) != 0 || (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */ - (r = sshbuf_put_u32(req, compat)) != 0) { + (r = sshbuf_put_u32(req, compat)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0) { error("%s: compose: %s", __func__, ssh_err(r)); goto out; } @@ -228,7 +252,7 @@ sshsk_sign(const char *provider, struct sshkey *key, r = SSH_ERR_ALLOC_FAIL; goto out; } - if ((r = client_converse(req, &resp)) != 0) + if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0) goto out; if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { @@ -259,8 +283,8 @@ sshsk_sign(const char *provider, struct sshkey *key, int sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp, - struct sshbuf *attest) + uint8_t flags, const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest) { int oerrno, r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL; @@ -289,12 +313,13 @@ sshsk_enroll(int type, const char *provider_path, const char *application, (r = sshbuf_put_cstring(req, provider_path)) != 0 || (r = sshbuf_put_cstring(req, application)) != 0 || (r = sshbuf_put_u8(req, flags)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0 || (r = sshbuf_put_stringb(req, challenge_buf)) != 0) { error("%s: compose: %s", __func__, ssh_err(r)); goto out; } - if ((r = client_converse(req, &resp)) != 0) + if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0) goto out; if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || @@ -358,7 +383,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, goto out; } - if ((r = client_converse(req, &resp)) != 0) + if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0) goto out; while (sshbuf_len(resp) != 0) { @@ -378,6 +403,8 @@ sshsk_load_resident(const char *provider_path, const char *pin, error("%s: recallocarray keys failed", __func__); goto out; } + debug("%s: keys[%zu]: %s %s", __func__, + nkeys, sshkey_type(key), key->sk_application); keys = tmp; keys[nkeys++] = key; key = NULL; diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index ac528cfcf..590ff8501 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.5 2019/12/30 09:21:59 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.6 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -50,6 +50,33 @@ #ifdef ENABLE_SK extern char *__progname; +static struct sshbuf *reply_error(int r, char *fmt, ...) + __attribute__((__format__ (printf, 2, 3))); + +static struct sshbuf * +reply_error(int r, char *fmt, ...) +{ + char *msg; + va_list ap; + struct sshbuf *resp; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + error("%s: %s", __progname, msg); + free(msg); + + if (r >= 0) + fatal("%s: invalid error code %d", __func__, r); + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if (sshbuf_put_u32(resp, SSH_SK_HELPER_ERROR) != 0 || + sshbuf_put_u32(resp, (u_int)-r) != 0) + fatal("%s: buffer error", __progname); + return resp; +} + static struct sshbuf * process_sign(struct sshbuf *req) { @@ -60,13 +87,14 @@ process_sign(struct sshbuf *req) const u_char *message; u_char *sig; size_t msglen, siglen; - char *provider; + char *provider, *pin; if ((r = sshbuf_froms(req, &kbuf)) != 0 || (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || (r = sshbuf_get_cstring(req, NULL, NULL)) != 0 || /* alg */ - (r = sshbuf_get_u32(req, &compat)) != 0) + (r = sshbuf_get_u32(req, &compat)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (sshbuf_len(req) != 0) fatal("%s: trailing data in request", __progname); @@ -80,19 +108,28 @@ process_sign(struct sshbuf *req) "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), provider, msglen, (u_long)compat); + if (*pin == 0) { + free(pin); + pin = NULL; + } + if ((r = sshsk_sign(provider, key, &sig, &siglen, - message, msglen, compat)) != 0) - fatal("Signing failed: %s", ssh_err(r)); + message, msglen, compat, pin)) != 0) { + resp = reply_error(r, "Signing failed: %s", ssh_err(r)); + goto out; + } if ((resp = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); - if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_SIGN)) != 0 || + (r = sshbuf_put_string(resp, sig, siglen)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); - + out: sshbuf_free(kbuf); free(provider); - + if (pin != NULL) + freezero(pin, strlen(pin)); return resp; } @@ -101,14 +138,12 @@ process_enroll(struct sshbuf *req) { int r; u_int type; - char *provider; - char *application; + char *provider, *application, *pin; uint8_t flags; struct sshbuf *challenge, *attest, *kbuf, *resp; struct sshkey *key; - if ((resp = sshbuf_new()) == NULL || - (attest = sshbuf_new()) == NULL || + if ((attest = sshbuf_new()) == NULL || (kbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); @@ -116,6 +151,7 @@ process_enroll(struct sshbuf *req) (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || (r = sshbuf_get_u8(req, &flags)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || (r = sshbuf_froms(req, &challenge)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (sshbuf_len(req) != 0) @@ -127,23 +163,35 @@ process_enroll(struct sshbuf *req) sshbuf_free(challenge); challenge = NULL; } + if (*pin == 0) { + free(pin); + pin = NULL; + } - if ((r = sshsk_enroll((int)type, provider, application, flags, - challenge, &key, attest)) != 0) - fatal("%s: sshsk_enroll failed: %s", __progname, ssh_err(r)); + if ((r = sshsk_enroll((int)type, provider, application, flags, pin, + challenge, &key, attest)) != 0) { + resp = reply_error(r, "Enrollment failed: %s", ssh_err(r)); + goto out; + } + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); if ((r = sshkey_private_serialize(key, kbuf)) != 0) fatal("%s: serialize private key: %s", __progname, ssh_err(r)); - if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 || + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_ENROLL)) != 0 || + (r = sshbuf_put_stringb(resp, kbuf)) != 0 || (r = sshbuf_put_stringb(resp, attest)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); + out: sshkey_free(key); sshbuf_free(kbuf); sshbuf_free(attest); sshbuf_free(challenge); free(provider); free(application); + if (pin != NULL) + freezero(pin, strlen(pin)); return resp; } @@ -157,8 +205,7 @@ process_load_resident(struct sshbuf *req) struct sshkey **keys = NULL; size_t nkeys = 0, i; - if ((resp = sshbuf_new()) == NULL || - (kbuf = sshbuf_new()) == NULL) + if ((kbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __progname); if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || @@ -167,9 +214,22 @@ process_load_resident(struct sshbuf *req) if (sshbuf_len(req) != 0) fatal("%s: trailing data in request", __progname); - if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) - fatal("%s: sshsk_load_resident failed: %s", - __progname, ssh_err(r)); + if (*pin == 0) { + free(pin); + pin = NULL; + } + + if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) { + resp = reply_error(r, " sshsk_load_resident failed: %s", + ssh_err(r)); + goto out; + } + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0) + fatal("%s: buffer error: %s", __progname, ssh_err(r)); for (i = 0; i < nkeys; i++) { debug("%s: key %zu %s %s", __func__, i, @@ -183,12 +243,14 @@ process_load_resident(struct sshbuf *req) fatal("%s: buffer error: %s", __progname, ssh_err(r)); } + out: for (i = 0; i < nkeys; i++) sshkey_free(keys[i]); free(keys); sshbuf_free(kbuf); free(provider); - freezero(pin, strlen(pin)); + if (pin != NULL) + freezero(pin, strlen(pin)); return resp; } diff --git a/ssh-sk.c b/ssh-sk.c index d48f34e30..a5d3c64d2 100644 --- a/ssh-sk.c +++ b/ssh-sk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.c,v 1.20 2019/12/30 09:21:16 djm Exp $ */ +/* $OpenBSD: ssh-sk.c,v 1.21 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -53,13 +53,14 @@ struct sshsk_provider { /* Enroll a U2F key (private key generation) */ int (*sk_enroll)(int alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, - struct sk_enroll_response **enroll_response); + const char *pin, struct sk_enroll_response **enroll_response); /* Sign a challenge */ int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, + struct sk_sign_response **sign_response); /* Enumerate resident keys */ int (*sk_load_resident_keys)(const char *pin, @@ -69,11 +70,11 @@ struct sshsk_provider { /* Built-in version */ int ssh_sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, - struct sk_enroll_response **enroll_response); + const char *pin, struct sk_enroll_response **enroll_response); int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, struct sk_sign_response **sign_response); int ssh_sk_load_resident_keys(const char *pin, struct sk_resident_key ***rks, size_t *nrks); @@ -326,8 +327,8 @@ sshsk_key_from_response(int alg, const char *application, uint8_t flags, int sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp, - struct sshbuf *attest) + uint8_t flags, const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest) { struct sshsk_provider *skp = NULL; struct sshkey *key = NULL; @@ -339,8 +340,9 @@ sshsk_enroll(int type, const char *provider_path, const char *application, int alg; debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, " - "challenge len %zu", __func__, provider_path, application, - flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf)); + "challenge len %zu%s", __func__, provider_path, application, + flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), + (pin != NULL && *pin != '\0') ? " with-pin" : ""); *keyp = NULL; if (attest) @@ -391,7 +393,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, /* XXX validate flags? */ /* enroll key */ if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, - flags, &resp)) != 0) { + flags, pin, &resp)) != 0) { error("Security key provider %s returned failure %d", provider_path, r); r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */ @@ -504,7 +506,7 @@ sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) int sshsk_sign(const char *provider_path, struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, - u_int compat) + u_int compat, const char *pin) { struct sshsk_provider *skp = NULL; int r = SSH_ERR_INTERNAL_ERROR; @@ -513,8 +515,9 @@ sshsk_sign(const char *provider_path, struct sshkey *key, struct sshbuf *inner_sig = NULL, *sig = NULL; uint8_t message[32]; - debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__, - provider_path, sshkey_type(key), key->sk_flags); + debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, + provider_path, sshkey_type(key), key->sk_flags, + (pin != NULL && *pin != '\0') ? " with-pin" : ""); if (sigp != NULL) *sigp = NULL; @@ -554,7 +557,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, if ((r = skp->sk_sign(alg, message, sizeof(message), key->sk_application, sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), - key->sk_flags, &resp)) != 0) { + key->sk_flags, pin, &resp)) != 0) { debug("%s: sk_sign failed with code %d", __func__, r); goto out; } diff --git a/ssh-sk.h b/ssh-sk.h index 1afe839db..348759a98 100644 --- a/ssh-sk.h +++ b/ssh-sk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.h,v 1.7 2019/12/30 09:21:16 djm Exp $ */ +/* $OpenBSD: ssh-sk.h,v 1.8 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -21,6 +21,15 @@ struct sshbuf; struct sshkey; +/* Version of protocol expected from ssh-sk-helper */ +#define SSH_SK_HELPER_VERSION 3 + +/* ssh-sk-helper messages */ +#define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ +#define SSH_SK_HELPER_SIGN 1 +#define SSH_SK_HELPER_ENROLL 2 +#define SSH_SK_HELPER_LOAD_RESIDENT 3 + /* * Enroll (generate) a new security-key hosted private key of given type * via the specified provider middleware. @@ -32,8 +41,8 @@ struct sshkey; * information is placed there. */ int sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp, - struct sshbuf *attest); + uint8_t flags, const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest); /* * Calculate an ECDSA_SK or ED25519_SK signature using the specified key @@ -43,7 +52,7 @@ int sshsk_enroll(int type, const char *provider_path, const char *application, */ int sshsk_sign(const char *provider_path, struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, - u_int compat); + u_int compat, const char *pin); /* * Enumerates and loads all SSH-compatible resident keys from a security diff --git a/sshkey.c b/sshkey.c index 3bab2e893..6ccd0e0b9 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.97 2019/12/13 19:09:10 djm Exp $ */ +/* $OpenBSD: sshkey.c,v 1.98 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -2765,7 +2765,7 @@ sshkey_sign(struct sshkey *key, case KEY_ECDSA_SK_CERT: case KEY_ECDSA_SK: r = sshsk_sign(sk_provider, key, sigp, lenp, data, - datalen, compat); + datalen, compat, /* XXX PIN */ NULL); break; #ifdef WITH_XMSS case KEY_XMSS: diff --git a/sshkey.h b/sshkey.h index 699c6b817..71a3fddcb 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.43 2019/12/30 09:21:59 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.44 2019/12/30 09:23:28 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -51,14 +51,6 @@ #define SSH_RSA_MINIMUM_MODULUS_SIZE 1024 #define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20) -/* Version of protocol expected from ssh-sk-helper */ -#define SSH_SK_HELPER_VERSION 2 - -/* ssh-sk-helper messages */ -#define SSH_SK_HELPER_SIGN 1 -#define SSH_SK_HELPER_ENROLL 2 -#define SSH_SK_HELPER_LOAD_RESIDENT 3 - struct sshbuf; /* Key types */ -- cgit v1.2.3 From c312ca077cd2a6c15545cd6b4d34ee2f69289174 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 6 Jan 2020 02:00:46 +0000 Subject: upstream: Extends the SK API to accept a set of key/value options for all operations. These are intended to future-proof the API a little by making it easier to specify additional fields for without having to change the API version for each. At present, only two options are defined: one to explicitly specify the device for an operation (rather than accepting the middleware's autoselection) and another to specify the FIDO2 username that may be used when generating a resident key. These new options may be invoked at key generation time via ssh-keygen -O This also implements a suggestion from Markus to avoid "int" in favour of uint32_t for the algorithm argument in the API, to make implementation of ssh-sk-client/helper a little easier. feedback, fixes and ok markus@ OpenBSD-Commit-ID: 973ce11704609022ab36abbdeb6bc23c8001eabc --- PROTOCOL.u2f | 47 ++++++++++++-- sk-api.h | 23 ++++--- sk-usbhid.c | 194 ++++++++++++++++++++++++++++++++++++++++++++------------ ssh-add.c | 5 +- ssh-keygen.1 | 23 +++++-- ssh-keygen.c | 39 ++++++++---- ssh-sk-client.c | 14 ++-- ssh-sk-helper.c | 45 +++++++------ ssh-sk.c | 121 ++++++++++++++++++++++++++++++----- ssh-sk.h | 14 ++-- 10 files changed, 404 insertions(+), 121 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f index 5f44c3acc..fd0cd0de0 100644 --- a/PROTOCOL.u2f +++ b/PROTOCOL.u2f @@ -233,7 +233,7 @@ support for the common case of USB HID security keys internally. The middleware library need only expose a handful of functions: - #define SSH_SK_VERSION_MAJOR 0x00030000 /* API version */ + #define SSH_SK_VERSION_MAJOR 0x00040000 /* API version */ #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 /* Flags */ @@ -245,6 +245,11 @@ The middleware library need only expose a handful of functions: #define SSH_SK_ECDSA 0x00 #define SSH_SK_ED25519 0x01 + /* Error codes */ + #define SSH_SK_ERR_GENERAL -1 + #define SSH_SK_ERR_UNSUPPORTED -2 + #define SSH_SK_ERR_PIN_REQUIRED -3 + struct sk_enroll_response { uint8_t *public_key; size_t public_key_len; @@ -266,35 +271,63 @@ The middleware library need only expose a handful of functions: }; struct sk_resident_key { - uint8_t alg; + uint32_t alg; size_t slot; char *application; struct sk_enroll_response key; }; + struct sk_option { + char *name; + char *value; + uint8_t important; + }; + /* Return the version of the middleware API */ uint32_t sk_api_version(void); /* Enroll a U2F key (private key generation) */ - int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, + int sk_enroll(uint32_t alg, + const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, const char *pin, + struct sk_option **options, struct sk_enroll_response **enroll_response); /* Sign a challenge */ - int sk_sign(int alg, const uint8_t *message, size_t message_len, + int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, + uint8_t flags, const char *pin, struct sk_option **options, struct sk_sign_response **sign_response); /* Enumerate all resident keys */ - int sk_load_resident_keys(const char *pin, + int sk_load_resident_keys(const char *pin, struct sk_option **options, struct sk_resident_key ***rks, size_t *nrks); The SSH_SK_VERSION_MAJOR should be incremented for each incompatible API change. -In OpenSSH, these will be invoked by using a similar mechanism to +The options may be used to pass miscellaneous options to the middleware +as a NULL-terminated array of pointers to struct sk_option. The middleware +may ignore unsupported or unknown options unless the "important" flag is +set, in which case it should return failure if an unsupported option is +requested. + +At present the following options names are supported: + + "device" + + Specifies a specific FIDO device on which to perform the + operation. The value in this field is interpreted by the + middleware but it would be typical to specify a path to + a /dev node for the device in question. + + "user" + + Specifies the FIDO2 username used when enrolling a key, + overriding OpenSSH's default of using an all-zero username. + +In OpenSSH, the middleware will be invoked by using a similar mechanism to ssh-pkcs11-helper to provide address-space containment of the middleware from ssh-agent. diff --git a/sk-api.h b/sk-api.h index dc786d556..93d6a1229 100644 --- a/sk-api.h +++ b/sk-api.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sk-api.h,v 1.6 2019/12/30 09:24:45 djm Exp $ */ +/* $OpenBSD: sk-api.h,v 1.7 2020/01/06 02:00:46 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -58,30 +58,37 @@ struct sk_sign_response { }; struct sk_resident_key { - uint8_t alg; + uint32_t alg; size_t slot; char *application; struct sk_enroll_response key; }; -#define SSH_SK_VERSION_MAJOR 0x00030000 /* current API version */ +struct sk_option { + char *name; + char *value; + uint8_t required; +}; + +#define SSH_SK_VERSION_MAJOR 0x00040000 /* current API version */ #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 /* Return the version of the middleware API */ uint32_t sk_api_version(void); /* Enroll a U2F key (private key generation) */ -int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, +int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, const char *pin, - struct sk_enroll_response **enroll_response); + struct sk_option **options, struct sk_enroll_response **enroll_response); /* Sign a challenge */ -int sk_sign(int alg, const uint8_t *message, size_t message_len, +int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response); /* Enumerate all resident keys */ -int sk_load_resident_keys(const char *pin, +int sk_load_resident_keys(const char *pin, struct sk_option **options, struct sk_resident_key ***rks, size_t *nrks); #endif /* _SK_API_H */ diff --git a/sk-usbhid.c b/sk-usbhid.c index 22a4c5df5..2e1573c48 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c @@ -54,7 +54,7 @@ } while (0) #endif -#define SK_VERSION_MAJOR 0x00030000 /* current API version */ +#define SK_VERSION_MAJOR 0x00040000 /* current API version */ /* Flags */ #define SK_USER_PRESENCE_REQD 0x01 @@ -91,12 +91,18 @@ struct sk_sign_response { }; struct sk_resident_key { - uint8_t alg; + uint32_t alg; size_t slot; char *application; struct sk_enroll_response key; }; +struct sk_option { + char *name; + char *value; + uint8_t required; +}; + /* If building as part of OpenSSH, then rename exported functions */ #if !defined(SK_STANDALONE) #define sk_api_version ssh_sk_api_version @@ -109,17 +115,18 @@ struct sk_resident_key { uint32_t sk_api_version(void); /* Enroll a U2F key (private key generation) */ -int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, +int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, const char *pin, - struct sk_enroll_response **enroll_response); + struct sk_option **options, struct sk_enroll_response **enroll_response); /* Sign a challenge */ -int sk_sign(int alg, const uint8_t *message, size_t message_len, +int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, struct sk_sign_response **sign_response); + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response); /* Load resident keys */ -int sk_load_resident_keys(const char *pin, +int sk_load_resident_keys(const char *pin, struct sk_option **options, struct sk_resident_key ***rks, size_t *nrks); static void skdebug(const char *func, const char *fmt, ...) @@ -235,15 +242,27 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, /* Iterate over configured devices looking for a specific key handle */ static fido_dev_t * -find_device(const uint8_t *message, size_t message_len, const char *application, - const uint8_t *key_handle, size_t key_handle_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) { fido_dev_info_t *devlist = NULL; fido_dev_t *dev = NULL; size_t devlist_len = 0, i; - const char *path; int r; + if (path != NULL) { + if ((dev = fido_dev_new()) == NULL) { + skdebug(__func__, "fido_dev_new failed"); + return NULL; + } + if ((r = fido_dev_open(dev, path)) != FIDO_OK) { + skdebug(__func__, "fido_dev_open failed"); + fido_dev_free(&dev); + return NULL; + } + return dev; + } + if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { skdebug(__func__, "fido_dev_info_new failed"); goto out; @@ -402,7 +421,7 @@ pack_public_key_ed25519(const fido_cred_t *cred, } static int -pack_public_key(int alg, const fido_cred_t *cred, +pack_public_key(uint32_t alg, const fido_cred_t *cred, struct sk_enroll_response *response) { switch(alg) { @@ -431,10 +450,45 @@ fidoerr_to_skerr(int fidoerr) } } +static int +check_enroll_options(struct sk_option **options, char **devicep, + uint8_t *user_id, size_t user_id_len) +{ + size_t i; + + if (options == NULL) + return 0; + for (i = 0; options[i] != NULL; i++) { + if (strcmp(options[i]->name, "device") == 0) { + if ((*devicep = strdup(options[i]->value)) == NULL) { + skdebug(__func__, "strdup device failed"); + return -1; + } + skdebug(__func__, "requested device %s", *devicep); + } if (strcmp(options[i]->name, "user") == 0) { + if (strlcpy(user_id, options[i]->value, user_id_len) >= + user_id_len) { + skdebug(__func__, "user too long"); + return -1; + } + skdebug(__func__, "requested user %s", + (char *)user_id); + } else { + skdebug(__func__, "requested unsupported option %s", + options[i]->name); + if (options[i]->required) { + skdebug(__func__, "unknown required option"); + return -1; + } + } + } + return 0; +} + int -sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, +sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, const char *pin, - struct sk_enroll_response **enroll_response) + struct sk_option **options, struct sk_enroll_response **enroll_response) { fido_cred_t *cred = NULL; fido_dev_t *dev = NULL; @@ -454,6 +508,11 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, skdebug(__func__, "enroll_response == NULL"); goto out; } + memset(user_id, 0, sizeof(user_id)); + if (check_enroll_options(options, &device, + user_id, sizeof(user_id)) != 0) + goto out; /* error already logged */ + *enroll_response = NULL; switch(alg) { #ifdef WITH_OPENSSL @@ -468,7 +527,7 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, skdebug(__func__, "unsupported key type %d", alg); goto out; } - if ((device = pick_first_device()) == NULL) { + if (device == NULL && (device = pick_first_device()) == NULL) { skdebug(__func__, "pick_first_device failed"); goto out; } @@ -477,7 +536,6 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, skdebug(__func__, "fido_cred_new failed"); goto out; } - memset(user_id, 0, sizeof(user_id)); if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); goto out; @@ -654,7 +712,8 @@ pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) } static int -pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) +pack_sig(uint32_t alg, fido_assert_t *assert, + struct sk_sign_response *response) { switch(alg) { #ifdef WITH_OPENSSL @@ -668,13 +727,42 @@ pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) } } +/* Checks sk_options for sk_sign() and sk_load_resident_keys() */ +static int +check_sign_load_resident_options(struct sk_option **options, char **devicep) +{ + size_t i; + + if (options == NULL) + return 0; + for (i = 0; options[i] != NULL; i++) { + if (strcmp(options[i]->name, "device") == 0) { + if ((*devicep = strdup(options[i]->value)) == NULL) { + skdebug(__func__, "strdup device failed"); + return -1; + } + skdebug(__func__, "requested device %s", *devicep); + } else { + skdebug(__func__, "requested unsupported option %s", + options[i]->name); + if (options[i]->required) { + skdebug(__func__, "unknown required option"); + return -1; + } + } + } + return 0; +} + int -sk_sign(int alg, const uint8_t *message, size_t message_len, +sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, struct sk_sign_response **sign_response) + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response) { fido_assert_t *assert = NULL; + char *device = NULL; fido_dev_t *dev = NULL; struct sk_sign_response *response = NULL; int ret = SSH_SK_ERR_GENERAL; @@ -689,8 +777,10 @@ sk_sign(int alg, const uint8_t *message, size_t message_len, goto out; } *sign_response = NULL; - if ((dev = find_device(message, message_len, application, key_handle, - key_handle_len)) == NULL) { + if (check_sign_load_resident_options(options, &device) != 0) + goto out; /* error already logged */ + if ((dev = find_device(device, message, message_len, + application, key_handle, key_handle_len)) == NULL) { skdebug(__func__, "couldn't find device for key handle"); goto out; } @@ -737,6 +827,7 @@ sk_sign(int alg, const uint8_t *message, size_t message_len, response = NULL; ret = 0; out: + free(device); if (response != NULL) { free(response->sig_r); free(response->sig_s); @@ -789,6 +880,7 @@ read_rks(const char *devpath, const char *pin, } skdebug(__func__, "get metadata for %s failed: %s", devpath, fido_strerr(r)); + ret = fidoerr_to_skerr(r); goto out; } skdebug(__func__, "existing %llu, remaining %llu", @@ -904,7 +996,7 @@ read_rks(const char *devpath, const char *pin, } int -sk_load_resident_keys(const char *pin, +sk_load_resident_keys(const char *pin, struct sk_option **options, struct sk_resident_key ***rksp, size_t *nrksp) { int ret = SSH_SK_ERR_GENERAL, r = -1; @@ -912,39 +1004,57 @@ sk_load_resident_keys(const char *pin, size_t i, ndev = 0, nrks = 0; const fido_dev_info_t *di; struct sk_resident_key **rks = NULL; + char *device = NULL; *rksp = NULL; *nrksp = 0; - if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { - skdebug(__func__, "fido_dev_info_new failed"); - goto out; - } - if ((r = fido_dev_info_manifest(devlist, - MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { - skdebug(__func__, "fido_dev_info_manifest failed: %s", - fido_strerr(r)); - goto out; - } - for (i = 0; i < ndev; i++) { - if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { - skdebug(__func__, "no dev info at %zu", i); - continue; - } - skdebug(__func__, "trying %s", fido_dev_info_path(di)); - if ((r = read_rks(fido_dev_info_path(di), pin, - &rks, &nrks)) != 0) { + if (check_sign_load_resident_options(options, &device) != 0) + goto out; /* error already logged */ + if (device != NULL) { + skdebug(__func__, "trying %s", device); + if ((r = read_rks(device, pin, &rks, &nrks)) != 0) { skdebug(__func__, "read_rks failed for %s", fido_dev_info_path(di)); - continue; + ret = r; + goto out; + } + } else { + /* Try all devices */ + if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { + skdebug(__func__, "fido_dev_info_new failed"); + goto out; + } + if ((r = fido_dev_info_manifest(devlist, + MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { + skdebug(__func__, "fido_dev_info_manifest failed: %s", + fido_strerr(r)); + goto out; + } + for (i = 0; i < ndev; i++) { + if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { + skdebug(__func__, "no dev info at %zu", i); + continue; + } + skdebug(__func__, "trying %s", fido_dev_info_path(di)); + if ((r = read_rks(fido_dev_info_path(di), pin, + &rks, &nrks)) != 0) { + skdebug(__func__, "read_rks failed for %s", + fido_dev_info_path(di)); + /* remember last error */ + ret = r; + continue; + } } } - /* success */ - ret = 0; + /* success, unless we have no keys but a specific error */ + if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) + ret = 0; *rksp = rks; *nrksp = nrks; rks = NULL; nrks = 0; out: + free(device); for (i = 0; i < nrks; i++) { free(rks[i]->application); freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); diff --git a/ssh-add.c b/ssh-add.c index c25b57cc1..fbb2578dd 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.148 2019/12/30 09:22:49 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.149 2020/01/06 02:00:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -549,7 +549,8 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag) char *fp; pass = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); - if ((r = sshsk_load_resident(skprovider, pass, &keys, &nkeys)) != 0) { + if ((r = sshsk_load_resident(skprovider, NULL, pass, + &keys, &nkeys)) != 0) { error("Unable to load resident keys: %s", ssh_err(r)); return r; } diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 7b83a2240..92c516588 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.188 2020/01/03 07:33:33 jmc Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.189 2020/01/06 02:00:46 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: January 3 2020 $ +.Dd $Mdocdate: January 6 2020 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -462,8 +462,18 @@ section may be specified. .Pp When generating a key that will be hosted on a FIDO authenticator, this flag may be used to specify key-specific options. -Two FIDO authenticator options are supported at present: -.Pp +The FIDO authenticator options are supported at present are: +.Pp +.Cm application +overrides the default FIDO application/origin string of +.Dq ssh: . +This option may be useful when generating host or domain-specific resident +keys. +.Cm device +explicitly specify a device to generate the key on, rather than accepting +the authenticator middleware's automatic selection. +.Xr fido 4 +device to use, rather than letting the token middleware select one. .Cm no-touch-required indicates that the generated private key should not require touch events (user presence) when making signatures. @@ -478,6 +488,11 @@ Resident keys may be supported on FIDO2 tokens and typically require that a PIN be set on the token prior to generation. Resident keys may be loaded off the token using .Xr ssh-add 1 . +.Cm user +allows specification of a username to be associated with a resident key, +overriding the empty default username. +Specifying a username may be useful when generating multiple resident keys +for the same application name. .Pp The .Fl O diff --git a/ssh-keygen.c b/ssh-keygen.c index 7731339f7..d0ffa5cd7 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.381 2020/01/02 22:40:09 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.382 2020/01/06 02:00:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -2915,7 +2915,7 @@ skip_ssh_url_preamble(const char *s) } static int -do_download_sk(const char *skprovider) +do_download_sk(const char *skprovider, const char *device) { struct sshkey **keys; size_t nkeys, i; @@ -2927,7 +2927,8 @@ do_download_sk(const char *skprovider) fatal("Cannot download keys without provider"); pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); - if ((r = sshsk_load_resident(skprovider, pin, &keys, &nkeys)) != 0) { + if ((r = sshsk_load_resident(skprovider, device, pin, + &keys, &nkeys)) != 0) { freezero(pin, strlen(pin)); error("Unable to load resident keys: %s", ssh_err(r)); return -1; @@ -3067,6 +3068,7 @@ main(int argc, char **argv) int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; unsigned long long cert_serial = 0; char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; + char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; size_t i, nopts = 0; u_int32_t bits = 0; uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; @@ -3396,8 +3398,17 @@ main(int argc, char **argv) } if (pkcs11provider != NULL) do_download(pw); - if (download_sk) - return do_download_sk(sk_provider); + if (download_sk) { + for (i = 0; i < nopts; i++) { + if (strncasecmp(opts[i], "device=", 7) == 0) { + sk_device = xstrdup(opts[i] + 7); + } else { + fatal("Option \"%s\" is unsupported for " + "FIDO authenticator download", opts[i]); + } + } + return do_download_sk(sk_provider, sk_device); + } if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) @@ -3484,6 +3495,13 @@ main(int argc, char **argv) sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; } else if (strcasecmp(opts[i], "resident") == 0) { sk_flags |= SSH_SK_RESIDENT_KEY; + } else if (strncasecmp(opts[i], "device=", 7) == 0) { + sk_device = xstrdup(opts[i] + 7); + } else if (strncasecmp(opts[i], "user=", 5) == 0) { + sk_user = xstrdup(opts[i] + 5); + } else if (strncasecmp(opts[i], + "application=", 12) == 0) { + sk_application = xstrdup(opts[i] + 12); } else { fatal("Option \"%s\" is unsupported for " "FIDO authenticator enrollment", opts[i]); @@ -3495,14 +3513,11 @@ main(int argc, char **argv) } passphrase = NULL; for (i = 0 ; i < 3; i++) { - if (!quiet) { - printf("You may need to touch your security " - "key to authorize key generation.\n"); - } fflush(stdout); - r = sshsk_enroll(type, sk_provider, - cert_key_id == NULL ? "ssh:" : cert_key_id, - sk_flags, passphrase, NULL, &private, NULL); + r = sshsk_enroll(type, sk_provider, sk_device, + sk_application == NULL ? "ssh:" : sk_application, + sk_user, sk_flags, passphrase, NULL, + &private, NULL); if (r == 0) break; if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) diff --git a/ssh-sk-client.c b/ssh-sk-client.c index 0033a6655..d3d37f792 100644 --- a/ssh-sk-client.c +++ b/ssh-sk-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-client.c,v 1.3 2019/12/30 09:23:28 djm Exp $ */ +/* $OpenBSD: ssh-sk-client.c,v 1.4 2020/01/06 02:00:46 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -282,8 +282,9 @@ sshsk_sign(const char *provider, struct sshkey *key, } int -sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, const char *pin, struct sshbuf *challenge_buf, +sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, struct sshkey **keyp, struct sshbuf *attest) { int oerrno, r = SSH_ERR_INTERNAL_ERROR; @@ -311,7 +312,9 @@ sshsk_enroll(int type, const char *provider_path, const char *application, if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_ENROLL)) != 0 || (r = sshbuf_put_u32(req, (u_int)type)) != 0 || (r = sshbuf_put_cstring(req, provider_path)) != 0 || + (r = sshbuf_put_cstring(req, device)) != 0 || (r = sshbuf_put_cstring(req, application)) != 0 || + (r = sshbuf_put_cstring(req, userid)) != 0 || (r = sshbuf_put_u8(req, flags)) != 0 || (r = sshbuf_put_cstring(req, pin)) != 0 || (r = sshbuf_put_stringb(req, challenge_buf)) != 0) { @@ -358,8 +361,8 @@ sshsk_enroll(int type, const char *provider_path, const char *application, } int -sshsk_load_resident(const char *provider_path, const char *pin, - struct sshkey ***keysp, size_t *nkeysp) +sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, struct sshkey ***keysp, size_t *nkeysp) { int oerrno, r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; @@ -378,6 +381,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 || (r = sshbuf_put_cstring(req, provider_path)) != 0 || + (r = sshbuf_put_cstring(req, device)) != 0 || (r = sshbuf_put_cstring(req, pin)) != 0) { error("%s: compose: %s", __func__, ssh_err(r)); goto out; diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 590ff8501..85a461d53 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.6 2019/12/30 09:23:28 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.7 2020/01/06 02:00:46 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -77,6 +77,17 @@ reply_error(int r, char *fmt, ...) return resp; } +/* If the specified string is zero length, then free it and replace with NULL */ +static void +null_empty(char **s) +{ + if (s == NULL || *s == NULL || **s != '\0') + return; + + free(*s); + *s = NULL; +} + static struct sshbuf * process_sign(struct sshbuf *req) { @@ -108,10 +119,7 @@ process_sign(struct sshbuf *req) "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), provider, msglen, (u_long)compat); - if (*pin == 0) { - free(pin); - pin = NULL; - } + null_empty(&pin); if ((r = sshsk_sign(provider, key, &sig, &siglen, message, msglen, compat, pin)) != 0) { @@ -138,7 +146,7 @@ process_enroll(struct sshbuf *req) { int r; u_int type; - char *provider, *application, *pin; + char *provider, *application, *pin, *device, *userid; uint8_t flags; struct sshbuf *challenge, *attest, *kbuf, *resp; struct sshkey *key; @@ -149,7 +157,9 @@ process_enroll(struct sshbuf *req) if ((r = sshbuf_get_u32(req, &type)) != 0 || (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &userid, NULL)) != 0 || (r = sshbuf_get_u8(req, &flags)) != 0 || (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || (r = sshbuf_froms(req, &challenge)) != 0) @@ -163,13 +173,12 @@ process_enroll(struct sshbuf *req) sshbuf_free(challenge); challenge = NULL; } - if (*pin == 0) { - free(pin); - pin = NULL; - } + null_empty(&device); + null_empty(&userid); + null_empty(&pin); - if ((r = sshsk_enroll((int)type, provider, application, flags, pin, - challenge, &key, attest)) != 0) { + if ((r = sshsk_enroll((int)type, provider, device, application, userid, + flags, pin, challenge, &key, attest)) != 0) { resp = reply_error(r, "Enrollment failed: %s", ssh_err(r)); goto out; } @@ -200,7 +209,7 @@ static struct sshbuf * process_load_resident(struct sshbuf *req) { int r; - char *provider, *pin; + char *provider, *pin, *device; struct sshbuf *kbuf, *resp; struct sshkey **keys = NULL; size_t nkeys = 0, i; @@ -209,17 +218,17 @@ process_load_resident(struct sshbuf *req) fatal("%s: sshbuf_new failed", __progname); if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); if (sshbuf_len(req) != 0) fatal("%s: trailing data in request", __progname); - if (*pin == 0) { - free(pin); - pin = NULL; - } + null_empty(&device); + null_empty(&pin); - if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) { + if ((r = sshsk_load_resident(provider, device, pin, + &keys, &nkeys)) != 0) { resp = reply_error(r, " sshsk_load_resident failed: %s", ssh_err(r)); goto out; diff --git a/ssh-sk.c b/ssh-sk.c index b1d0d6c58..0ef52e299 100644 --- a/ssh-sk.c +++ b/ssh-sk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.c,v 1.23 2019/12/30 09:24:45 djm Exp $ */ +/* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -53,29 +53,32 @@ struct sshsk_provider { /* Enroll a U2F key (private key generation) */ int (*sk_enroll)(int alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, - const char *pin, struct sk_enroll_response **enroll_response); + const char *pin, struct sk_option **opts, + struct sk_enroll_response **enroll_response); /* Sign a challenge */ int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, + uint8_t flags, const char *pin, struct sk_option **opts, struct sk_sign_response **sign_response); /* Enumerate resident keys */ - int (*sk_load_resident_keys)(const char *pin, + int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, struct sk_resident_key ***rks, size_t *nrks); }; /* Built-in version */ int ssh_sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, const char *application, uint8_t flags, - const char *pin, struct sk_enroll_response **enroll_response); + const char *pin, struct sk_option **opts, + struct sk_enroll_response **enroll_response); int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, const char *application, const uint8_t *key_handle, size_t key_handle_len, - uint8_t flags, const char *pin, struct sk_sign_response **sign_response); -int ssh_sk_load_resident_keys(const char *pin, + uint8_t flags, const char *pin, struct sk_option **opts, + struct sk_sign_response **sign_response); +int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, struct sk_resident_key ***rks, size_t *nrks); static void @@ -339,9 +342,80 @@ skerr_to_ssherr(int skerr) } } +static void +sshsk_free_options(struct sk_option **opts) +{ + size_t i; + + if (opts == NULL) + return; + for (i = 0; opts[i] != NULL; i++) { + free(opts[i]->name); + free(opts[i]->value); + free(opts[i]); + } + free(opts); +} + +static int +sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, + const char *name, const char *value, uint8_t required) +{ + struct sk_option **opts = *optsp; + size_t nopts = *noptsp; + + if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ + sizeof(*opts))) == NULL) { + error("%s: array alloc failed", __func__); + return SSH_ERR_ALLOC_FAIL; + } + *optsp = opts; + *noptsp = nopts + 1; + if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { + error("%s: alloc failed", __func__); + return SSH_ERR_ALLOC_FAIL; + } + if ((opts[nopts]->name = strdup(name)) == NULL || + (opts[nopts]->value = strdup(value)) == NULL) { + error("%s: alloc failed", __func__); + return SSH_ERR_ALLOC_FAIL; + } + opts[nopts]->required = required; + return 0; +} + +static int +make_options(const char *device, const char *user_id, + struct sk_option ***optsp) +{ + struct sk_option **opts = NULL; + size_t nopts = 0; + int r, ret = SSH_ERR_INTERNAL_ERROR; + + if (device != NULL && + (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { + ret = r; + goto out; + } + if (user_id != NULL && + (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { + ret = r; + goto out; + } + /* success */ + *optsp = opts; + opts = NULL; + nopts = 0; + ret = 0; + out: + sshsk_free_options(opts); + return ret; +} + int -sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, const char *pin, struct sshbuf *challenge_buf, +sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, struct sshkey **keyp, struct sshbuf *attest) { struct sshsk_provider *skp = NULL; @@ -350,17 +424,23 @@ sshsk_enroll(int type, const char *provider_path, const char *application, const u_char *challenge; size_t challenge_len; struct sk_enroll_response *resp = NULL; + struct sk_option **opts = NULL; int r = SSH_ERR_INTERNAL_ERROR; int alg; - debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, " - "challenge len %zu%s", __func__, provider_path, application, - flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), + debug("%s: provider \"%s\", device \"%s\", application \"%s\", " + "userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__, + provider_path, device, application, userid, flags, + challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), (pin != NULL && *pin != '\0') ? " with-pin" : ""); *keyp = NULL; if (attest) sshbuf_reset(attest); + + if ((r = make_options(device, userid, &opts)) != 0) + goto out; + switch (type) { #ifdef WITH_OPENSSL case KEY_ECDSA_SK: @@ -407,7 +487,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, /* XXX validate flags? */ /* enroll key */ if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, - flags, pin, &resp)) != 0) { + flags, pin, opts, &resp)) != 0) { error("Security key provider \"%s\" returned failure %d", provider_path, r); r = skerr_to_ssherr(r); @@ -437,6 +517,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, key = NULL; /* transferred */ r = 0; out: + sshsk_free_options(opts); sshsk_free(skp); sshkey_free(key); sshsk_free_enroll_response(resp); @@ -528,6 +609,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, struct sk_sign_response *resp = NULL; struct sshbuf *inner_sig = NULL, *sig = NULL; uint8_t message[32]; + struct sk_option **opts = NULL; debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, provider_path, sshkey_type(key), key->sk_flags, @@ -571,7 +653,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, if ((r = skp->sk_sign(alg, message, sizeof(message), key->sk_application, sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), - key->sk_flags, pin, &resp)) != 0) { + key->sk_flags, pin, opts, &resp)) != 0) { debug("%s: sk_sign failed with code %d", __func__, r); r = skerr_to_ssherr(r); goto out; @@ -617,6 +699,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, /* success */ r = 0; out: + sshsk_free_options(opts); explicit_bzero(message, sizeof(message)); sshsk_free(skp); sshsk_free_sign_response(resp); @@ -645,8 +728,8 @@ sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) } int -sshsk_load_resident(const char *provider_path, const char *pin, - struct sshkey ***keysp, size_t *nkeysp) +sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, struct sshkey ***keysp, size_t *nkeysp) { struct sshsk_provider *skp = NULL; int r = SSH_ERR_INTERNAL_ERROR; @@ -654,6 +737,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, size_t i, nrks = 0, nkeys = 0; struct sshkey *key = NULL, **keys = NULL, **tmp; uint8_t flags; + struct sk_option **opts = NULL; debug("%s: provider \"%s\"%s", __func__, provider_path, (pin != NULL && *pin != '\0') ? ", have-pin": ""); @@ -663,11 +747,13 @@ sshsk_load_resident(const char *provider_path, const char *pin, *keysp = NULL; *nkeysp = 0; + if ((r = make_options(device, NULL, &opts)) != 0) + goto out; if ((skp = sshsk_open(provider_path)) == NULL) { r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ goto out; } - if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) { + if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { error("Security key provider \"%s\" returned failure %d", provider_path, r); r = skerr_to_ssherr(r); @@ -710,6 +796,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, nkeys = 0; r = 0; out: + sshsk_free_options(opts); sshsk_free(skp); sshsk_free_sk_resident_keys(rks, nrks); sshkey_free(key); diff --git a/ssh-sk.h b/ssh-sk.h index 348759a98..ea9ff6e1a 100644 --- a/ssh-sk.h +++ b/ssh-sk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.h,v 1.8 2019/12/30 09:23:28 djm Exp $ */ +/* $OpenBSD: ssh-sk.h,v 1.9 2020/01/06 02:00:47 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -20,9 +20,10 @@ struct sshbuf; struct sshkey; +struct sk_option; /* Version of protocol expected from ssh-sk-helper */ -#define SSH_SK_HELPER_VERSION 3 +#define SSH_SK_HELPER_VERSION 4 /* ssh-sk-helper messages */ #define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ @@ -40,8 +41,9 @@ struct sshkey; * If successful and the attest_data buffer is not NULL then attestation * information is placed there. */ -int sshsk_enroll(int type, const char *provider_path, const char *application, - uint8_t flags, const char *pin, struct sshbuf *challenge_buf, +int sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, struct sshkey **keyp, struct sshbuf *attest); /* @@ -60,8 +62,8 @@ int sshsk_sign(const char *provider_path, struct sshkey *key, * * Returns 0 on success or a ssherr.h error code on failure. */ -int sshsk_load_resident(const char *provider_path, const char *pin, - struct sshkey ***keysp, size_t *nkeysp); +int sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, struct sshkey ***keysp, size_t *nkeysp); #endif /* _SSH_SK_H */ -- cgit v1.2.3 From 57b181eaf2d34fd0a1b51ab30cb6983df784de5a Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Fri, 10 Jan 2020 23:43:26 +0000 Subject: upstream: pass the log-on-stderr flag and log level through to ssh-sk-helper, making debugging a bit easier. ok markus@ OpenBSD-Commit-ID: 2e7aea6bf5770d3f38b7c7bba891069256c5a49a --- ssh-sk-client.c | 40 +++++++++++++++++++++++----------------- ssh-sk-helper.c | 19 ++++++++++++------- ssh-sk.h | 4 ++-- 3 files changed, 37 insertions(+), 26 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/ssh-sk-client.c b/ssh-sk-client.c index d3d37f792..9121570dc 100644 --- a/ssh-sk-client.c +++ b/ssh-sk-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-client.c,v 1.4 2020/01/06 02:00:46 djm Exp $ */ +/* $OpenBSD: ssh-sk-client.c,v 1.5 2020/01/10 23:43:26 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -129,24 +129,32 @@ reap_helper(pid_t pid) } static int -client_converse(struct sshbuf *req, struct sshbuf **respp, u_int msg) +client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type) { - int oerrno, fd, r2, r = SSH_ERR_INTERNAL_ERROR; - u_int rmsg, rerr; + int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR; + u_int rtype, rerr; pid_t pid; u_char version; void (*osigchld)(int); - struct sshbuf *resp = NULL; + struct sshbuf *req = NULL, *resp = NULL; *respp = NULL; if ((r = start_helper(&fd, &pid, &osigchld)) != 0) return r; - if ((resp = sshbuf_new()) == NULL) { + if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } - + /* Request preamble: type, log_on_stderr, log_level */ + ll = log_level_get(); + if ((r = sshbuf_put_u32(req, type)) != 0 || + (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 || + (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 || + (r = sshbuf_putb(req, msg)) != 0) { + error("%s: build: %s", __func__, ssh_err(r)); + goto out; + } if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) { error("%s: send: %s", __func__, ssh_err(r)); goto out; @@ -165,11 +173,11 @@ client_converse(struct sshbuf *req, struct sshbuf **respp, u_int msg) r = SSH_ERR_INVALID_FORMAT; goto out; } - if ((r = sshbuf_get_u32(resp, &rmsg)) != 0) { + if ((r = sshbuf_get_u32(resp, &rtype)) != 0) { error("%s: parse message type: %s", __func__, ssh_err(r)); goto out; } - if (rmsg == SSH_SK_HELPER_ERROR) { + if (rtype == SSH_SK_HELPER_ERROR) { if ((r = sshbuf_get_u32(resp, &rerr)) != 0) { error("%s: parse error: %s", __func__, ssh_err(r)); goto out; @@ -181,9 +189,9 @@ client_converse(struct sshbuf *req, struct sshbuf **respp, u_int msg) else r = -(int)rerr; goto out; - } else if (rmsg != msg) { + } else if (rtype != type) { error("%s: helper returned incorrect message type %u, " - "expecting %u", __func__, rmsg, msg); + "expecting %u", __func__, rtype, type); r = SSH_ERR_INTERNAL_ERROR; goto out; } @@ -202,6 +210,7 @@ client_converse(struct sshbuf *req, struct sshbuf **respp, u_int msg) *respp = resp; resp = NULL; } + sshbuf_free(req); sshbuf_free(resp); signal(SIGCHLD, osigchld); errno = oerrno; @@ -235,8 +244,7 @@ sshsk_sign(const char *provider, struct sshkey *key, error("%s: serialize private key: %s", __func__, ssh_err(r)); goto out; } - if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_SIGN)) != 0 || - (r = sshbuf_put_stringb(req, kbuf)) != 0 || + if ((r = sshbuf_put_stringb(req, kbuf)) != 0 || (r = sshbuf_put_cstring(req, provider)) != 0 || (r = sshbuf_put_string(req, data, datalen)) != 0 || (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */ @@ -309,8 +317,7 @@ sshsk_enroll(int type, const char *provider_path, const char *device, goto out; } - if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_ENROLL)) != 0 || - (r = sshbuf_put_u32(req, (u_int)type)) != 0 || + if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 || (r = sshbuf_put_cstring(req, provider_path)) != 0 || (r = sshbuf_put_cstring(req, device)) != 0 || (r = sshbuf_put_cstring(req, application)) != 0 || @@ -379,8 +386,7 @@ sshsk_load_resident(const char *provider_path, const char *device, goto out; } - if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 || - (r = sshbuf_put_cstring(req, provider_path)) != 0 || + if ((r = sshbuf_put_cstring(req, provider_path)) != 0 || (r = sshbuf_put_cstring(req, device)) != 0 || (r = sshbuf_put_cstring(req, pin)) != 0) { error("%s: compose: %s", __func__, ssh_err(r)); diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 85a461d53..a4be9d369 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.7 2020/01/06 02:00:46 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.8 2020/01/10 23:43:26 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -269,9 +269,9 @@ main(int argc, char **argv) SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; LogLevel log_level = SYSLOG_LEVEL_ERROR; struct sshbuf *req, *resp; - int in, out, ch, r, log_stderr = 0; - u_int rtype; - uint8_t version; + int in, out, ch, r, vflag = 0; + u_int rtype, ll = 0; + uint8_t version, log_stderr = 0; sanitise_stdfd(); log_init(__progname, log_level, log_facility, log_stderr); @@ -279,7 +279,7 @@ main(int argc, char **argv) while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { case 'v': - log_stderr = 1; + vflag = 1; if (log_level == SYSLOG_LEVEL_ERROR) log_level = SYSLOG_LEVEL_DEBUG1; else if (log_level < SYSLOG_LEVEL_DEBUG3) @@ -290,7 +290,7 @@ main(int argc, char **argv) exit(1); } } - log_init(__progname, log_level, log_facility, log_stderr); + log_init(__progname, log_level, log_facility, vflag); /* * Rearrange our file descriptors a little; we don't trust the @@ -317,9 +317,14 @@ main(int argc, char **argv) version, SSH_SK_HELPER_VERSION); } - if ((r = sshbuf_get_u32(req, &rtype)) != 0) + if ((r = sshbuf_get_u32(req, &rtype)) != 0 || + (r = sshbuf_get_u8(req, &log_stderr)) != 0 || + (r = sshbuf_get_u32(req, &ll)) != 0) fatal("%s: buffer error: %s", __progname, ssh_err(r)); + if (!vflag && log_level_name((LogLevel)ll) != NULL) + log_init(__progname, (LogLevel)ll, log_facility, log_stderr); + switch (rtype) { case SSH_SK_HELPER_SIGN: resp = process_sign(req); diff --git a/ssh-sk.h b/ssh-sk.h index ea9ff6e1a..0f566bbc3 100644 --- a/ssh-sk.h +++ b/ssh-sk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.h,v 1.9 2020/01/06 02:00:47 djm Exp $ */ +/* $OpenBSD: ssh-sk.h,v 1.10 2020/01/10 23:43:26 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -23,7 +23,7 @@ struct sshkey; struct sk_option; /* Version of protocol expected from ssh-sk-helper */ -#define SSH_SK_HELPER_VERSION 4 +#define SSH_SK_HELPER_VERSION 5 /* ssh-sk-helper messages */ #define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ -- cgit v1.2.3 From 59d01f1d720ebede4da42882f592d1093dac7adc Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sat, 25 Jan 2020 23:13:09 +0000 Subject: upstream: improve the error message for u2f enrollment errors by making ssh-keygen be solely responsible for printing the error message and convertint some more common error responses from the middleware to a useful ssherr.h status code. more detail remains visible via -v of course. also remove indepedent copy of sk-api.h declarations in sk-usbhid.c and just include it. feedback & ok markus@ OpenBSD-Commit-ID: a4a8ffa870d9a3e0cfd76544bcdeef5c9fb1f1bb --- PROTOCOL.u2f | 1 + sk-api.h | 3 +- sk-usbhid.c | 99 ++++++++++++++++----------------------------------------- ssh-keygen.c | 4 +-- ssh-sk-helper.c | 4 +-- ssh-sk.c | 6 ++-- ssherr.c | 4 ++- ssherr.h | 3 +- 8 files changed, 43 insertions(+), 81 deletions(-) (limited to 'ssh-sk-helper.c') diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f index fd0cd0de0..58f75ba28 100644 --- a/PROTOCOL.u2f +++ b/PROTOCOL.u2f @@ -249,6 +249,7 @@ The middleware library need only expose a handful of functions: #define SSH_SK_ERR_GENERAL -1 #define SSH_SK_ERR_UNSUPPORTED -2 #define SSH_SK_ERR_PIN_REQUIRED -3 + #define SSH_SK_ERR_DEVICE_NOT_FOUND -4 struct sk_enroll_response { uint8_t *public_key; diff --git a/sk-api.h b/sk-api.h index 93d6a1229..170fd4470 100644 --- a/sk-api.h +++ b/sk-api.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sk-api.h,v 1.7 2020/01/06 02:00:46 djm Exp $ */ +/* $OpenBSD: sk-api.h,v 1.8 2020/01/25 23:13:09 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -36,6 +36,7 @@ #define SSH_SK_ERR_GENERAL -1 #define SSH_SK_ERR_UNSUPPORTED -2 #define SSH_SK_ERR_PIN_REQUIRED -3 +#define SSH_SK_ERR_DEVICE_NOT_FOUND -4 struct sk_enroll_response { uint8_t *public_key; diff --git a/sk-usbhid.c b/sk-usbhid.c index cf783e205..2148e1d79 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c @@ -37,9 +37,19 @@ #include #ifndef SK_STANDALONE -#include "log.h" -#include "xmalloc.h" -#endif +# include "log.h" +# include "xmalloc.h" +/* + * If building as part of OpenSSH, then rename exported functions. + * This must be done before including sk-api.h. + */ +# define sk_api_version ssh_sk_api_version +# define sk_enroll ssh_sk_enroll +# define sk_sign ssh_sk_sign +# define sk_load_resident_keys ssh_sk_load_resident_keys +#endif /* !SK_STANDALONE */ + +#include "sk-api.h" /* #define SK_DEBUG 1 */ @@ -54,63 +64,6 @@ } while (0) #endif -#define SK_VERSION_MAJOR 0x00040000 /* current API version */ - -/* Flags */ -#define SK_USER_PRESENCE_REQD 0x01 -#define SK_USER_VERIFICATION_REQD 0x04 -#define SK_RESIDENT_KEY 0x20 - -/* Algs */ -#define SK_ECDSA 0x00 -#define SK_ED25519 0x01 - -/* Error codes */ -#define SSH_SK_ERR_GENERAL -1 -#define SSH_SK_ERR_UNSUPPORTED -2 -#define SSH_SK_ERR_PIN_REQUIRED -3 - -struct sk_enroll_response { - uint8_t *public_key; - size_t public_key_len; - uint8_t *key_handle; - size_t key_handle_len; - uint8_t *signature; - size_t signature_len; - uint8_t *attestation_cert; - size_t attestation_cert_len; -}; - -struct sk_sign_response { - uint8_t flags; - uint32_t counter; - uint8_t *sig_r; - size_t sig_r_len; - uint8_t *sig_s; - size_t sig_s_len; -}; - -struct sk_resident_key { - uint32_t alg; - size_t slot; - char *application; - struct sk_enroll_response key; -}; - -struct sk_option { - char *name; - char *value; - uint8_t required; -}; - -/* If building as part of OpenSSH, then rename exported functions */ -#if !defined(SK_STANDALONE) -#define sk_api_version ssh_sk_api_version -#define sk_enroll ssh_sk_enroll -#define sk_sign ssh_sk_sign -#define sk_load_resident_keys ssh_sk_load_resident_keys -#endif - /* Return the version of the middleware API */ uint32_t sk_api_version(void); @@ -161,7 +114,7 @@ skdebug(const char *func, const char *fmt, ...) uint32_t sk_api_version(void) { - return SK_VERSION_MAJOR; + return SSH_SK_VERSION_MAJOR; } /* Select the first identified FIDO device attached to the system */ @@ -426,10 +379,10 @@ pack_public_key(uint32_t alg, const fido_cred_t *cred, { switch(alg) { #ifdef WITH_OPENSSL - case SK_ECDSA: + case SSH_SK_ECDSA: return pack_public_key_ecdsa(cred, response); #endif /* WITH_OPENSSL */ - case SK_ED25519: + case SSH_SK_ED25519: return pack_public_key_ed25519(cred, response); default: return -1; @@ -441,6 +394,7 @@ fidoerr_to_skerr(int fidoerr) { switch (fidoerr) { case FIDO_ERR_UNSUPPORTED_OPTION: + case FIDO_ERR_UNSUPPORTED_ALGORITHM: return SSH_SK_ERR_UNSUPPORTED; case FIDO_ERR_PIN_REQUIRED: case FIDO_ERR_PIN_INVALID: @@ -516,11 +470,11 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, *enroll_response = NULL; switch(alg) { #ifdef WITH_OPENSSL - case SK_ECDSA: + case SSH_SK_ECDSA: cose_alg = COSE_ES256; break; #endif /* WITH_OPENSSL */ - case SK_ED25519: + case SSH_SK_ED25519: cose_alg = COSE_EDDSA; break; default: @@ -528,6 +482,7 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, goto out; } if (device == NULL && (device = pick_first_device()) == NULL) { + ret = SSH_SK_ERR_DEVICE_NOT_FOUND; skdebug(__func__, "pick_first_device failed"); goto out; } @@ -546,7 +501,7 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, fido_strerr(r)); goto out; } - if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ? + if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ? FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); goto out; @@ -717,10 +672,10 @@ pack_sig(uint32_t alg, fido_assert_t *assert, { switch(alg) { #ifdef WITH_OPENSSL - case SK_ECDSA: + case SSH_SK_ECDSA: return pack_sig_ecdsa(assert, response); #endif /* WITH_OPENSSL */ - case SK_ED25519: + case SSH_SK_ED25519: return pack_sig_ed25519(assert, response); default: return -1; @@ -804,7 +759,7 @@ sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, goto out; } if ((r = fido_assert_set_up(assert, - (flags & SK_USER_PRESENCE_REQD) ? + (flags & SSH_SK_USER_PRESENCE_REQD) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); goto out; @@ -951,15 +906,15 @@ read_rks(const char *devpath, const char *pin, switch (fido_cred_type(cred)) { case COSE_ES256: - srk->alg = SK_ECDSA; + srk->alg = SSH_SK_ECDSA; break; case COSE_EDDSA: - srk->alg = SK_ED25519; + srk->alg = SSH_SK_ED25519; break; default: skdebug(__func__, "unsupported key type %d", fido_cred_type(cred)); - goto out; + goto out; /* XXX free rk and continue */ } if ((r = pack_public_key(srk->alg, cred, diff --git a/ssh-keygen.c b/ssh-keygen.c index 29013a20f..8df55f2c2 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.393 2020/01/25 23:02:13 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.394 2020/01/25 23:13:09 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -3579,7 +3579,7 @@ main(int argc, char **argv) if (r == 0) break; if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) - exit(1); /* error message already printed */ + fatal("Key enrollment failed: %s", ssh_err(r)); if (passphrase != NULL) freezero(passphrase, strlen(passphrase)); passphrase = read_passphrase("Enter PIN for security " diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index a4be9d369..2f93ad716 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk-helper.c,v 1.8 2020/01/10 23:43:26 djm Exp $ */ +/* $OpenBSD: ssh-sk-helper.c,v 1.9 2020/01/25 23:13:09 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -63,7 +63,7 @@ reply_error(int r, char *fmt, ...) va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); - error("%s: %s", __progname, msg); + debug("%s: %s", __progname, msg); free(msg); if (r >= 0) diff --git a/ssh-sk.c b/ssh-sk.c index 3f5eed62d..a8d4de832 100644 --- a/ssh-sk.c +++ b/ssh-sk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */ +/* $OpenBSD: ssh-sk.c,v 1.25 2020/01/25 23:13:09 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -338,6 +338,8 @@ skerr_to_ssherr(int skerr) return SSH_ERR_FEATURE_UNSUPPORTED; case SSH_SK_ERR_PIN_REQUIRED: return SSH_ERR_KEY_WRONG_PASSPHRASE; + case SSH_SK_ERR_DEVICE_NOT_FOUND: + return SSH_ERR_DEVICE_NOT_FOUND; case SSH_SK_ERR_GENERAL: default: return SSH_ERR_INVALID_FORMAT; @@ -490,7 +492,7 @@ sshsk_enroll(int type, const char *provider_path, const char *device, /* enroll key */ if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, flags, pin, opts, &resp)) != 0) { - error("Security key provider \"%s\" returned failure %d", + debug("%s: provider \"%s\" returned failure %d", __func__, provider_path, r); r = skerr_to_ssherr(r); goto out; diff --git a/ssherr.c b/ssherr.c index 38974f51b..bd954aadd 100644 --- a/ssherr.c +++ b/ssherr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssherr.c,v 1.9 2019/12/30 09:24:45 djm Exp $ */ +/* $OpenBSD: ssherr.c,v 1.10 2020/01/25 23:13:09 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -143,6 +143,8 @@ ssh_err(int n) return "signature algorithm not supported"; case SSH_ERR_FEATURE_UNSUPPORTED: return "requested feature not supported"; + case SSH_ERR_DEVICE_NOT_FOUND: + return "device not found"; default: return "unknown error"; } diff --git a/ssherr.h b/ssherr.h index 520bd4964..085e75274 100644 --- a/ssherr.h +++ b/ssherr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssherr.h,v 1.7 2019/12/30 09:24:45 djm Exp $ */ +/* $OpenBSD: ssherr.h,v 1.8 2020/01/25 23:13:09 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -81,6 +81,7 @@ #define SSH_ERR_NUMBER_TOO_LARGE -57 #define SSH_ERR_SIGN_ALG_UNSUPPORTED -58 #define SSH_ERR_FEATURE_UNSUPPORTED -59 +#define SSH_ERR_DEVICE_NOT_FOUND -60 /* Translate a numeric error code to a human-readable error string */ const char *ssh_err(int n); -- cgit v1.2.3