diff --git a/Makefile.in b/Makefile.in index bac522ad2..c4b4b9357 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,6 +24,7 @@ ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass SFTP_SERVER=$(libexecdir)/sftp-server SSH_KEYSIGN=$(libexecdir)/ssh-keysign SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper +SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper PRIVSEP_PATH=@PRIVSEP_PATH@ SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ STRIP_OPT=@STRIP_OPT@ @@ -35,6 +36,7 @@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \ -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ + -D_PATH_SSH_SK_HELPER=\"$(SSH_SK_HELPER)\" \ -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" @@ -60,7 +62,7 @@ EXEEXT=@EXEEXT@ MANFMT=@MANFMT@ MKDIR_P=@MKDIR_P@ -TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) +TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) XMSS_OBJS=\ ssh-xmss.o \ @@ -199,6 +201,9 @@ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o c ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) +ssh-sk-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-sk-helper.o + $(LD) -o $@ ssh-sk-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -350,6 +355,7 @@ install-files: $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-sk-helper$(EXEEXT) $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 @@ -426,6 +432,7 @@ uninstall: -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT) -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 @@ -616,10 +623,10 @@ interop-tests t-exec file-tests: regress-prep regress-binaries $(TARGETS) TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \ TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \ TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ + TEST_SSH_SSHSKHELPER="$${BUILDDIR}/ssh-sk-helper"; \ TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \ TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \ TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \ - TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ TEST_SSH_PLINK="plink"; \ TEST_SSH_PUTTYGEN="puttygen"; \ TEST_SSH_CONCH="conch"; \ diff --git a/pathnames.h b/pathnames.h index 2e0c7b15b..3a1bd1977 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.29 2019/10/31 21:15:14 djm Exp $ */ +/* $OpenBSD: pathnames.h,v 1.30 2019/10/31 21:22:01 djm Exp $ */ /* * Author: Tatu Ylonen @@ -133,6 +133,11 @@ #define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" #endif +/* Location of ssh-sk-helper to support keys in security keys */ +#ifndef _PATH_SSH_SK_HELPER +#define _PATH_SSH_SK_HELPER "/usr/libexec/ssh-sk-helper" +#endif + /* xauth for X11 forwarding */ #ifndef _PATH_XAUTH #define _PATH_XAUTH "/usr/X11R6/bin/xauth" diff --git a/ssh-agent.1 b/ssh-agent.1 index 83b2b41c8..7719384fe 100644 --- a/ssh-agent.1 +++ b/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.64 2016/11/30 06:54:26 jmc Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.65 2019/10/31 21:22:01 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,7 +34,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: November 30 2016 $ +.Dd $Mdocdate: October 31 2019 $ .Dt SSH-AGENT 1 .Os .Sh NAME @@ -46,7 +46,7 @@ .Op Fl \&Dd .Op Fl a Ar bind_address .Op Fl E Ar fingerprint_hash -.Op Fl P Ar pkcs11_whitelist +.Op Fl P Ar provider_whitelist .Op Fl t Ar life .Op Ar command Op Ar arg ... .Nm ssh-agent @@ -122,15 +122,17 @@ The default is Kill the current agent (given by the .Ev SSH_AGENT_PID environment variable). -.It Fl P Ar pkcs11_whitelist -Specify a pattern-list of acceptable paths for PKCS#11 shared libraries -that may be added using the +.It Fl P Ar provider_whitelist +Specify a pattern-list of acceptable paths for PKCS#11 and security key shared +libraries that may be used with the .Fl s -option to +or +.Fl S +options to .Xr ssh-add 1 . -The default is to allow loading PKCS#11 libraries from +The default is to allow loading libraries from .Dq /usr/lib/*,/usr/local/lib/* . -PKCS#11 libraries that do not match the whitelist will be refused. +Libraries that do not match the whitelist will be refused. See PATTERNS in .Xr ssh_config 5 for a description of pattern-list syntax. diff --git a/ssh-agent.c b/ssh-agent.c index e500591a9..6bf9536fb 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.237 2019/06/28 13:35:04 deraadt Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.238 2019/10/31 21:22:01 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef HAVE_SYS_TIME_H # include #endif @@ -85,13 +86,13 @@ #include "digest.h" #include "ssherr.h" #include "match.h" - -#ifdef ENABLE_PKCS11 +#include "msg.h" +#include "pathnames.h" #include "ssh-pkcs11.h" -#endif +#include "ssh-sk.h" -#ifndef DEFAULT_PKCS11_WHITELIST -# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" +#ifndef DEFAULT_PROVIDER_WHITELIST +# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" #endif /* Maximum accepted message length */ @@ -123,6 +124,7 @@ typedef struct identity { char *provider; time_t death; u_int confirm; + char *sk_provider; } Identity; struct idtable { @@ -146,8 +148,8 @@ pid_t cleanup_pid = 0; char socket_name[PATH_MAX]; char socket_dir[PATH_MAX]; -/* PKCS#11 path whitelist */ -static char *pkcs11_whitelist; +/* PKCS#11/Security key path whitelist */ +static char *provider_whitelist; /* locking */ #define LOCK_SIZE 32 @@ -189,6 +191,7 @@ free_identity(Identity *id) sshkey_free(id->key); free(id->provider); free(id->comment); + free(id->sk_provider); free(id); } @@ -278,6 +281,121 @@ agent_decode_alg(struct sshkey *key, u_int flags) return NULL; } +static int +provider_sign(const char *provider, struct sshkey *key, + u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, u_int compat) +{ + int status, pair[2], r = SSH_ERR_INTERNAL_ERROR; + pid_t pid; + char *helper, *verbosity = NULL; + struct sshbuf *kbuf, *req, *resp; + u_char version; + + debug3("%s: start for provider %s", __func__, provider); + + *sigp = NULL; + *lenp = 0; + + helper = getenv("SSH_SK_HELPER"); + if (helper == NULL || strlen(helper) == 0) + helper = _PATH_SSH_SK_HELPER; + if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) + verbosity = "-vvv"; + + /* Start helper */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + error("socketpair: %s", strerror(errno)); + return SSH_ERR_SYSTEM_ERROR; + } + if ((pid = fork()) == -1) { + error("fork: %s", strerror(errno)); + close(pair[0]); + close(pair[1]); + return SSH_ERR_SYSTEM_ERROR; + } + if (pid == 0) { + if ((dup2(pair[1], STDIN_FILENO) == -1) || + (dup2(pair[1], STDOUT_FILENO) == -1)) + fatal("%s: dup2: %s", __func__, ssh_err(r)); + close(pair[0]); + close(pair[1]); + closefrom(STDERR_FILENO + 1); + debug("%s: starting %s %s", __func__, helper, + verbosity == NULL ? "" : verbosity); + execlp(helper, helper, verbosity, (char *)NULL); + fatal("%s: execlp: %s", __func__, strerror(errno)); + } + close(pair[1]); + + if ((kbuf = sshbuf_new()) == NULL || + (req = sshbuf_new()) == NULL || + (resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + if ((r = sshkey_private_serialize(key, kbuf)) != 0 || + (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_u32(req, compat)) != 0) + fatal("%s: compose: %s", __func__, ssh_err(r)); + if ((r = ssh_msg_send(pair[0], SSH_SK_HELPER_VERSION, req)) != 0) { + error("%s: send: %s", __func__, ssh_err(r)); + goto out; + } + if ((r = ssh_msg_recv(pair[0], resp)) != 0) { + error("%s: receive: %s", __func__, ssh_err(r)); + goto out; + } + if ((r = sshbuf_get_u8(resp, &version)) != 0) { + error("%s: parse version: %s", __func__, ssh_err(r)); + goto out; + } + if (version != SSH_SK_HELPER_VERSION) { + error("%s: unsupported version: got %u, expected %u", + __func__, version, SSH_SK_HELPER_VERSION); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { + error("%s: parse signature: %s", __func__, ssh_err(r)); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(resp) != 0) { + error("%s: trailing data in response", __func__); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + r = 0; + out: + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) + fatal("%s: waitpid: %s", __func__, ssh_err(r)); + } + if (!WIFEXITED(status)) { + error("%s: helper %s exited abnormally", __func__, helper); + if (r == 0) + r = SSH_ERR_SYSTEM_ERROR; + } else if (WEXITSTATUS(status) != 0) { + error("%s: helper %s exited with non-zero exit status", + __func__, helper); + if (r == 0) + r = SSH_ERR_SYSTEM_ERROR; + } + if (r != 0) { + freezero(*sigp, *lenp); + *sigp = NULL; + *lenp = 0; + } + sshbuf_free(kbuf); + sshbuf_free(req); + sshbuf_free(resp); + return r; +} + /* ssh2 only */ static void process_sign_request2(SocketEntry *e) @@ -308,10 +426,19 @@ process_sign_request2(SocketEntry *e) verbose("%s: user refused key", __func__); goto send; } - if ((r = sshkey_sign(id->key, &signature, &slen, - data, dlen, agent_decode_alg(key, flags), compat)) != 0) { - error("%s: sshkey_sign: %s", __func__, ssh_err(r)); - goto send; + if (id->sk_provider != NULL) { + if ((r = provider_sign(id->sk_provider, id->key, &signature, + &slen, data, dlen, agent_decode_alg(key, flags), + compat)) != 0) { + error("%s: sshkey_sign: %s", __func__, ssh_err(r)); + goto send; + } + } else { + if ((r = sshkey_sign(id->key, &signature, &slen, + data, dlen, agent_decode_alg(key, flags), compat)) != 0) { + error("%s: sshkey_sign: %s", __func__, ssh_err(r)); + goto send; + } } /* Success */ ok = 0; @@ -411,7 +538,7 @@ process_add_identity(SocketEntry *e) Identity *id; int success = 0, confirm = 0; u_int seconds, maxsign; - char *comment = NULL; + char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL; time_t death = 0; struct sshkey *k = NULL; u_char ctype; @@ -456,15 +583,58 @@ process_add_identity(SocketEntry *e) goto err; } break; + case SSH_AGENT_CONSTRAIN_EXTENSION: + if ((r = sshbuf_get_cstring(e->request, + &ext_name, NULL)) != 0) { + error("%s: cannot parse extension: %s", + __func__, ssh_err(r)); + goto err; + } + debug("%s: constraint ext %s", __func__, ext_name); + if (strcmp(ext_name, "sk-provider@openssh.com") == 0) { + if (sk_provider != NULL) { + error("%s already set", ext_name); + goto err; + } + if ((r = sshbuf_get_cstring(e->request, + &sk_provider, NULL)) != 0) { + error("%s: cannot parse %s: %s", + __func__, ext_name, ssh_err(r)); + goto err; + } + } else { + error("%s: unsupported constraint \"%s\"", + __func__, ext_name); + goto err; + } + free(ext_name); + break; default: error("%s: Unknown constraint %d", __func__, ctype); err: + free(sk_provider); + free(ext_name); sshbuf_reset(e->request); free(comment); sshkey_free(k); goto send; } } + if (sk_provider != NULL) { + if (sshkey_type_plain(k->type) != KEY_ECDSA_SK) { + error("Cannot add provider: %s is not a security key", + sshkey_type(k)); + free(sk_provider); + goto send; + } + if (match_pattern_list(sk_provider, + provider_whitelist, 0) != 1) { + error("Refusing add key: provider %s not whitelisted", + sk_provider); + free(sk_provider); + goto send; + } + } success = 1; if (lifetime && !death) @@ -478,11 +648,21 @@ process_add_identity(SocketEntry *e) /* key state might have been updated */ sshkey_free(id->key); free(id->comment); + free(id->sk_provider); } id->key = k; id->comment = comment; id->death = death; id->confirm = confirm; + id->sk_provider = sk_provider; + + if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal("%s: sshkey_fingerprint failed", __func__); + debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) " + "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment, + seconds, confirm, sk_provider == NULL ? "none" : sk_provider); + free(fp); send: send_status(e, success); } @@ -600,7 +780,7 @@ process_add_smartcard_key(SocketEntry *e) provider, strerror(errno)); goto send; } - if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { + if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) { verbose("refusing PKCS#11 add of \"%.100s\": " "provider not whitelisted", canonical_provider); goto send; @@ -1079,7 +1259,7 @@ usage(void) { fprintf(stderr, "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" - " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" + " [-P provider_whitelist] [-t life] [command [arg ...]]\n" " ssh-agent [-c | -s] -k\n"); exit(1); } @@ -1137,9 +1317,9 @@ main(int ac, char **av) k_flag++; break; case 'P': - if (pkcs11_whitelist != NULL) + if (provider_whitelist != NULL) fatal("-P option already specified"); - pkcs11_whitelist = xstrdup(optarg); + provider_whitelist = xstrdup(optarg); break; case 's': if (c_flag) @@ -1175,8 +1355,8 @@ main(int ac, char **av) if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) usage(); - if (pkcs11_whitelist == NULL) - pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); + if (provider_whitelist == NULL) + provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST); if (ac == 0 && !c_flag && !s_flag) { shell = getenv("SHELL"); 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); +} diff --git a/ssh-sk.h b/ssh-sk.h index 7c1d2b927..5033e6f68 100644 --- a/ssh-sk.h +++ b/ssh-sk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-sk.h,v 1.1 2019/10/31 21:16:20 djm Exp $ */ +/* $OpenBSD: ssh-sk.h,v 1.2 2019/10/31 21:22:01 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -21,6 +21,9 @@ struct sshbuf; struct sshkey; +/* Version of protocol between ssh-agent and ssh-sk-helper */ +#define SSH_SK_HELPER_VERSION 1 + /* * Enroll (generate) a new security-key hosted private key via the specified * provider middleware.