[openssh-commits] [openssh] 11/14: upstream: ssh-agent support for U2F/FIDO keys

git+noreply at mindrot.org git+noreply at mindrot.org
Fri Nov 1 09:47:20 AEDT 2019


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 07da39f71d36fb547749a5b16aa8892e621a7e4a
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Thu Oct 31 21:22:01 2019 +0000

    upstream: ssh-agent support for U2F/FIDO keys
    
    feedback & ok markus@
    
    OpenBSD-Commit-ID: bb544a44bc32e45d2ec8bf652db2046f38360acb
---
 Makefile.in     |  11 ++-
 pathnames.h     |   7 +-
 ssh-agent.1     |  20 +++---
 ssh-agent.c     | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 ssh-sk-helper.c | 143 +++++++++++++++++++++++++++++++++++++
 ssh-sk.h        |   5 +-
 6 files changed, 372 insertions(+), 32 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index bac522ad..c4b4b935 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 2e0c7b15..3a1bd197 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 <ylo at cs.hut.fi>
@@ -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 83b2b41c..7719384f 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 <ylo at cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, 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 e500591a..6bf9536f 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 <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -41,6 +41,7 @@
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
+#include <sys/wait.h>
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #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 at 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 00000000..0a0c92a4
--- /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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#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 7c1d2b92..5033e6f6 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.

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list