summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-10-31 21:22:01 +0000
committerDamien Miller <djm@mindrot.org>2019-11-01 09:46:09 +1100
commit07da39f71d36fb547749a5b16aa8892e621a7e4a (patch)
treedd75cbd723102d887bc11f781cc0a23eee6b2f2f
parenteebec620c9519c4839d781c4d5b6082152998f82 (diff)
upstream: ssh-agent support for U2F/FIDO keys
feedback & ok markus@ OpenBSD-Commit-ID: bb544a44bc32e45d2ec8bf652db2046f38360acb
-rw-r--r--Makefile.in11
-rw-r--r--pathnames.h7
-rw-r--r--ssh-agent.120
-rw-r--r--ssh-agent.c218
-rw-r--r--ssh-sk-helper.c143
-rw-r--r--ssh-sk.h5
6 files changed, 372 insertions, 32 deletions
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
24SFTP_SERVER=$(libexecdir)/sftp-server 24SFTP_SERVER=$(libexecdir)/sftp-server
25SSH_KEYSIGN=$(libexecdir)/ssh-keysign 25SSH_KEYSIGN=$(libexecdir)/ssh-keysign
26SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper 26SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
27SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper
27PRIVSEP_PATH=@PRIVSEP_PATH@ 28PRIVSEP_PATH=@PRIVSEP_PATH@
28SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ 29SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
29STRIP_OPT=@STRIP_OPT@ 30STRIP_OPT=@STRIP_OPT@
@@ -35,6 +36,7 @@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \
35 -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ 36 -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \
36 -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ 37 -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \
37 -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ 38 -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
39 -D_PATH_SSH_SK_HELPER=\"$(SSH_SK_HELPER)\" \
38 -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ 40 -D_PATH_SSH_PIDDIR=\"$(piddir)\" \
39 -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" 41 -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\"
40 42
@@ -60,7 +62,7 @@ EXEEXT=@EXEEXT@
60MANFMT=@MANFMT@ 62MANFMT=@MANFMT@
61MKDIR_P=@MKDIR_P@ 63MKDIR_P=@MKDIR_P@
62 64
63TARGETS=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) 65TARGETS=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)
64 66
65XMSS_OBJS=\ 67XMSS_OBJS=\
66 ssh-xmss.o \ 68 ssh-xmss.o \
@@ -199,6 +201,9 @@ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o c
199ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o 201ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o
200 $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) 202 $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
201 203
204ssh-sk-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-sk-helper.o
205 $(LD) -o $@ ssh-sk-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
206
202ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o 207ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o
203 $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) 208 $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS)
204 209
@@ -350,6 +355,7 @@ install-files:
350 $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) 355 $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
351 $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) 356 $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
352 $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) 357 $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
358 $(INSTALL) -m 0755 $(STRIP_OPT) ssh-sk-helper$(EXEEXT) $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT)
353 $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) 359 $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT)
354 $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) 360 $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
355 $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 361 $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
@@ -426,6 +432,7 @@ uninstall:
426 -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) 432 -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
427 -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) 433 -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
428 -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) 434 -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
435 -rm -f $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT)
429 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 436 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
430 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 437 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
431 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 438 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1
@@ -616,10 +623,10 @@ interop-tests t-exec file-tests: regress-prep regress-binaries $(TARGETS)
616 TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \ 623 TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \
617 TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \ 624 TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \
618 TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ 625 TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
626 TEST_SSH_SSHSKHELPER="$${BUILDDIR}/ssh-sk-helper"; \
619 TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \ 627 TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \
620 TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \ 628 TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \
621 TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \ 629 TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \
622 TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
623 TEST_SSH_PLINK="plink"; \ 630 TEST_SSH_PLINK="plink"; \
624 TEST_SSH_PUTTYGEN="puttygen"; \ 631 TEST_SSH_PUTTYGEN="puttygen"; \
625 TEST_SSH_CONCH="conch"; \ 632 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 @@
1/* $OpenBSD: pathnames.h,v 1.29 2019/10/31 21:15:14 djm Exp $ */ 1/* $OpenBSD: pathnames.h,v 1.30 2019/10/31 21:22:01 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -133,6 +133,11 @@
133#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" 133#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper"
134#endif 134#endif
135 135
136/* Location of ssh-sk-helper to support keys in security keys */
137#ifndef _PATH_SSH_SK_HELPER
138#define _PATH_SSH_SK_HELPER "/usr/libexec/ssh-sk-helper"
139#endif
140
136/* xauth for X11 forwarding */ 141/* xauth for X11 forwarding */
137#ifndef _PATH_XAUTH 142#ifndef _PATH_XAUTH
138#define _PATH_XAUTH "/usr/X11R6/bin/xauth" 143#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 @@
1.\" $OpenBSD: ssh-agent.1,v 1.64 2016/11/30 06:54:26 jmc Exp $ 1.\" $OpenBSD: ssh-agent.1,v 1.65 2019/10/31 21:22:01 djm Exp $
2.\" 2.\"
3.\" Author: Tatu Ylonen <ylo@cs.hut.fi> 3.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -34,7 +34,7 @@
34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36.\" 36.\"
37.Dd $Mdocdate: November 30 2016 $ 37.Dd $Mdocdate: October 31 2019 $
38.Dt SSH-AGENT 1 38.Dt SSH-AGENT 1
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -46,7 +46,7 @@
46.Op Fl \&Dd 46.Op Fl \&Dd
47.Op Fl a Ar bind_address 47.Op Fl a Ar bind_address
48.Op Fl E Ar fingerprint_hash 48.Op Fl E Ar fingerprint_hash
49.Op Fl P Ar pkcs11_whitelist 49.Op Fl P Ar provider_whitelist
50.Op Fl t Ar life 50.Op Fl t Ar life
51.Op Ar command Op Ar arg ... 51.Op Ar command Op Ar arg ...
52.Nm ssh-agent 52.Nm ssh-agent
@@ -122,15 +122,17 @@ The default is
122Kill the current agent (given by the 122Kill the current agent (given by the
123.Ev SSH_AGENT_PID 123.Ev SSH_AGENT_PID
124environment variable). 124environment variable).
125.It Fl P Ar pkcs11_whitelist 125.It Fl P Ar provider_whitelist
126Specify a pattern-list of acceptable paths for PKCS#11 shared libraries 126Specify a pattern-list of acceptable paths for PKCS#11 and security key shared
127that may be added using the 127libraries that may be used with the
128.Fl s 128.Fl s
129option to 129or
130.Fl S
131options to
130.Xr ssh-add 1 . 132.Xr ssh-add 1 .
131The default is to allow loading PKCS#11 libraries from 133The default is to allow loading libraries from
132.Dq /usr/lib/*,/usr/local/lib/* . 134.Dq /usr/lib/*,/usr/local/lib/* .
133PKCS#11 libraries that do not match the whitelist will be refused. 135Libraries that do not match the whitelist will be refused.
134See PATTERNS in 136See PATTERNS in
135.Xr ssh_config 5 137.Xr ssh_config 5
136for a description of pattern-list syntax. 138for 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 @@
1/* $OpenBSD: ssh-agent.c,v 1.237 2019/06/28 13:35:04 deraadt Exp $ */ 1/* $OpenBSD: ssh-agent.c,v 1.238 2019/10/31 21:22:01 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -41,6 +41,7 @@
41#include <sys/resource.h> 41#include <sys/resource.h>
42#include <sys/stat.h> 42#include <sys/stat.h>
43#include <sys/socket.h> 43#include <sys/socket.h>
44#include <sys/wait.h>
44#ifdef HAVE_SYS_TIME_H 45#ifdef HAVE_SYS_TIME_H
45# include <sys/time.h> 46# include <sys/time.h>
46#endif 47#endif
@@ -85,13 +86,13 @@
85#include "digest.h" 86#include "digest.h"
86#include "ssherr.h" 87#include "ssherr.h"
87#include "match.h" 88#include "match.h"
88 89#include "msg.h"
89#ifdef ENABLE_PKCS11 90#include "pathnames.h"
90#include "ssh-pkcs11.h" 91#include "ssh-pkcs11.h"
91#endif 92#include "ssh-sk.h"
92 93
93#ifndef DEFAULT_PKCS11_WHITELIST 94#ifndef DEFAULT_PROVIDER_WHITELIST
94# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" 95# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
95#endif 96#endif
96 97
97/* Maximum accepted message length */ 98/* Maximum accepted message length */
@@ -123,6 +124,7 @@ typedef struct identity {
123 char *provider; 124 char *provider;
124 time_t death; 125 time_t death;
125 u_int confirm; 126 u_int confirm;
127 char *sk_provider;
126} Identity; 128} Identity;
127 129
128struct idtable { 130struct idtable {
@@ -146,8 +148,8 @@ pid_t cleanup_pid = 0;
146char socket_name[PATH_MAX]; 148char socket_name[PATH_MAX];
147char socket_dir[PATH_MAX]; 149char socket_dir[PATH_MAX];
148 150
149/* PKCS#11 path whitelist */ 151/* PKCS#11/Security key path whitelist */
150static char *pkcs11_whitelist; 152static char *provider_whitelist;
151 153
152/* locking */ 154/* locking */
153#define LOCK_SIZE 32 155#define LOCK_SIZE 32
@@ -189,6 +191,7 @@ free_identity(Identity *id)
189 sshkey_free(id->key); 191 sshkey_free(id->key);
190 free(id->provider); 192 free(id->provider);
191 free(id->comment); 193 free(id->comment);
194 free(id->sk_provider);
192 free(id); 195 free(id);
193} 196}
194 197
@@ -278,6 +281,121 @@ agent_decode_alg(struct sshkey *key, u_int flags)
278 return NULL; 281 return NULL;
279} 282}
280 283
284static int
285provider_sign(const char *provider, struct sshkey *key,
286 u_char **sigp, size_t *lenp,
287 const u_char *data, size_t datalen,
288 const char *alg, u_int compat)
289{
290 int status, pair[2], r = SSH_ERR_INTERNAL_ERROR;
291 pid_t pid;
292 char *helper, *verbosity = NULL;
293 struct sshbuf *kbuf, *req, *resp;
294 u_char version;
295
296 debug3("%s: start for provider %s", __func__, provider);
297
298 *sigp = NULL;
299 *lenp = 0;
300
301 helper = getenv("SSH_SK_HELPER");
302 if (helper == NULL || strlen(helper) == 0)
303 helper = _PATH_SSH_SK_HELPER;
304 if (log_level_get() >= SYSLOG_LEVEL_DEBUG1)
305 verbosity = "-vvv";
306
307 /* Start helper */
308 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
309 error("socketpair: %s", strerror(errno));
310 return SSH_ERR_SYSTEM_ERROR;
311 }
312 if ((pid = fork()) == -1) {
313 error("fork: %s", strerror(errno));
314 close(pair[0]);
315 close(pair[1]);
316 return SSH_ERR_SYSTEM_ERROR;
317 }
318 if (pid == 0) {
319 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
320 (dup2(pair[1], STDOUT_FILENO) == -1))
321 fatal("%s: dup2: %s", __func__, ssh_err(r));
322 close(pair[0]);
323 close(pair[1]);
324 closefrom(STDERR_FILENO + 1);
325 debug("%s: starting %s %s", __func__, helper,
326 verbosity == NULL ? "" : verbosity);
327 execlp(helper, helper, verbosity, (char *)NULL);
328 fatal("%s: execlp: %s", __func__, strerror(errno));
329 }
330 close(pair[1]);
331
332 if ((kbuf = sshbuf_new()) == NULL ||
333 (req = sshbuf_new()) == NULL ||
334 (resp = sshbuf_new()) == NULL)
335 fatal("%s: sshbuf_new failed", __func__);
336
337 if ((r = sshkey_private_serialize(key, kbuf)) != 0 ||
338 (r = sshbuf_put_stringb(req, kbuf)) != 0 ||
339 (r = sshbuf_put_cstring(req, provider)) != 0 ||
340 (r = sshbuf_put_string(req, data, datalen)) != 0 ||
341 (r = sshbuf_put_u32(req, compat)) != 0)
342 fatal("%s: compose: %s", __func__, ssh_err(r));
343 if ((r = ssh_msg_send(pair[0], SSH_SK_HELPER_VERSION, req)) != 0) {
344 error("%s: send: %s", __func__, ssh_err(r));
345 goto out;
346 }
347 if ((r = ssh_msg_recv(pair[0], resp)) != 0) {
348 error("%s: receive: %s", __func__, ssh_err(r));
349 goto out;
350 }
351 if ((r = sshbuf_get_u8(resp, &version)) != 0) {
352 error("%s: parse version: %s", __func__, ssh_err(r));
353 goto out;
354 }
355 if (version != SSH_SK_HELPER_VERSION) {
356 error("%s: unsupported version: got %u, expected %u",
357 __func__, version, SSH_SK_HELPER_VERSION);
358 r = SSH_ERR_INVALID_FORMAT;
359 goto out;
360 }
361 if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
362 error("%s: parse signature: %s", __func__, ssh_err(r));
363 r = SSH_ERR_INVALID_FORMAT;
364 goto out;
365 }
366 if (sshbuf_len(resp) != 0) {
367 error("%s: trailing data in response", __func__);
368 r = SSH_ERR_INVALID_FORMAT;
369 goto out;
370 }
371 /* success */
372 r = 0;
373 out:
374 while (waitpid(pid, &status, 0) == -1) {
375 if (errno != EINTR)
376 fatal("%s: waitpid: %s", __func__, ssh_err(r));
377 }
378 if (!WIFEXITED(status)) {
379 error("%s: helper %s exited abnormally", __func__, helper);
380 if (r == 0)
381 r = SSH_ERR_SYSTEM_ERROR;
382 } else if (WEXITSTATUS(status) != 0) {
383 error("%s: helper %s exited with non-zero exit status",
384 __func__, helper);
385 if (r == 0)
386 r = SSH_ERR_SYSTEM_ERROR;
387 }
388 if (r != 0) {
389 freezero(*sigp, *lenp);
390 *sigp = NULL;
391 *lenp = 0;
392 }
393 sshbuf_free(kbuf);
394 sshbuf_free(req);
395 sshbuf_free(resp);
396 return r;
397}
398
281/* ssh2 only */ 399/* ssh2 only */
282static void 400static void
283process_sign_request2(SocketEntry *e) 401process_sign_request2(SocketEntry *e)
@@ -308,10 +426,19 @@ process_sign_request2(SocketEntry *e)
308 verbose("%s: user refused key", __func__); 426 verbose("%s: user refused key", __func__);
309 goto send; 427 goto send;
310 } 428 }
311 if ((r = sshkey_sign(id->key, &signature, &slen, 429 if (id->sk_provider != NULL) {
312 data, dlen, agent_decode_alg(key, flags), compat)) != 0) { 430 if ((r = provider_sign(id->sk_provider, id->key, &signature,
313 error("%s: sshkey_sign: %s", __func__, ssh_err(r)); 431 &slen, data, dlen, agent_decode_alg(key, flags),
314 goto send; 432 compat)) != 0) {
433 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
434 goto send;
435 }
436 } else {
437 if ((r = sshkey_sign(id->key, &signature, &slen,
438 data, dlen, agent_decode_alg(key, flags), compat)) != 0) {
439 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
440 goto send;
441 }
315 } 442 }
316 /* Success */ 443 /* Success */
317 ok = 0; 444 ok = 0;
@@ -411,7 +538,7 @@ process_add_identity(SocketEntry *e)
411 Identity *id; 538 Identity *id;
412 int success = 0, confirm = 0; 539 int success = 0, confirm = 0;
413 u_int seconds, maxsign; 540 u_int seconds, maxsign;
414 char *comment = NULL; 541 char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
415 time_t death = 0; 542 time_t death = 0;
416 struct sshkey *k = NULL; 543 struct sshkey *k = NULL;
417 u_char ctype; 544 u_char ctype;
@@ -456,15 +583,58 @@ process_add_identity(SocketEntry *e)
456 goto err; 583 goto err;
457 } 584 }
458 break; 585 break;
586 case SSH_AGENT_CONSTRAIN_EXTENSION:
587 if ((r = sshbuf_get_cstring(e->request,
588 &ext_name, NULL)) != 0) {
589 error("%s: cannot parse extension: %s",
590 __func__, ssh_err(r));
591 goto err;
592 }
593 debug("%s: constraint ext %s", __func__, ext_name);
594 if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
595 if (sk_provider != NULL) {
596 error("%s already set", ext_name);
597 goto err;
598 }
599 if ((r = sshbuf_get_cstring(e->request,
600 &sk_provider, NULL)) != 0) {
601 error("%s: cannot parse %s: %s",
602 __func__, ext_name, ssh_err(r));
603 goto err;
604 }
605 } else {
606 error("%s: unsupported constraint \"%s\"",
607 __func__, ext_name);
608 goto err;
609 }
610 free(ext_name);
611 break;
459 default: 612 default:
460 error("%s: Unknown constraint %d", __func__, ctype); 613 error("%s: Unknown constraint %d", __func__, ctype);
461 err: 614 err:
615 free(sk_provider);
616 free(ext_name);
462 sshbuf_reset(e->request); 617 sshbuf_reset(e->request);
463 free(comment); 618 free(comment);
464 sshkey_free(k); 619 sshkey_free(k);
465 goto send; 620 goto send;
466 } 621 }
467 } 622 }
623 if (sk_provider != NULL) {
624 if (sshkey_type_plain(k->type) != KEY_ECDSA_SK) {
625 error("Cannot add provider: %s is not a security key",
626 sshkey_type(k));
627 free(sk_provider);
628 goto send;
629 }
630 if (match_pattern_list(sk_provider,
631 provider_whitelist, 0) != 1) {
632 error("Refusing add key: provider %s not whitelisted",
633 sk_provider);
634 free(sk_provider);
635 goto send;
636 }
637 }
468 638
469 success = 1; 639 success = 1;
470 if (lifetime && !death) 640 if (lifetime && !death)
@@ -478,11 +648,21 @@ process_add_identity(SocketEntry *e)
478 /* key state might have been updated */ 648 /* key state might have been updated */
479 sshkey_free(id->key); 649 sshkey_free(id->key);
480 free(id->comment); 650 free(id->comment);
651 free(id->sk_provider);
481 } 652 }
482 id->key = k; 653 id->key = k;
483 id->comment = comment; 654 id->comment = comment;
484 id->death = death; 655 id->death = death;
485 id->confirm = confirm; 656 id->confirm = confirm;
657 id->sk_provider = sk_provider;
658
659 if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
660 SSH_FP_DEFAULT)) == NULL)
661 fatal("%s: sshkey_fingerprint failed", __func__);
662 debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) "
663 "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment,
664 seconds, confirm, sk_provider == NULL ? "none" : sk_provider);
665 free(fp);
486send: 666send:
487 send_status(e, success); 667 send_status(e, success);
488} 668}
@@ -600,7 +780,7 @@ process_add_smartcard_key(SocketEntry *e)
600 provider, strerror(errno)); 780 provider, strerror(errno));
601 goto send; 781 goto send;
602 } 782 }
603 if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { 783 if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) {
604 verbose("refusing PKCS#11 add of \"%.100s\": " 784 verbose("refusing PKCS#11 add of \"%.100s\": "
605 "provider not whitelisted", canonical_provider); 785 "provider not whitelisted", canonical_provider);
606 goto send; 786 goto send;
@@ -1079,7 +1259,7 @@ usage(void)
1079{ 1259{
1080 fprintf(stderr, 1260 fprintf(stderr,
1081 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" 1261 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
1082 " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" 1262 " [-P provider_whitelist] [-t life] [command [arg ...]]\n"
1083 " ssh-agent [-c | -s] -k\n"); 1263 " ssh-agent [-c | -s] -k\n");
1084 exit(1); 1264 exit(1);
1085} 1265}
@@ -1137,9 +1317,9 @@ main(int ac, char **av)
1137 k_flag++; 1317 k_flag++;
1138 break; 1318 break;
1139 case 'P': 1319 case 'P':
1140 if (pkcs11_whitelist != NULL) 1320 if (provider_whitelist != NULL)
1141 fatal("-P option already specified"); 1321 fatal("-P option already specified");
1142 pkcs11_whitelist = xstrdup(optarg); 1322 provider_whitelist = xstrdup(optarg);
1143 break; 1323 break;
1144 case 's': 1324 case 's':
1145 if (c_flag) 1325 if (c_flag)
@@ -1175,8 +1355,8 @@ main(int ac, char **av)
1175 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) 1355 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
1176 usage(); 1356 usage();
1177 1357
1178 if (pkcs11_whitelist == NULL) 1358 if (provider_whitelist == NULL)
1179 pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); 1359 provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST);
1180 1360
1181 if (ac == 0 && !c_flag && !s_flag) { 1361 if (ac == 0 && !c_flag && !s_flag) {
1182 shell = getenv("SHELL"); 1362 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 @@
1/* $OpenBSD: ssh-sk-helper.c,v 1.1 2019/10/31 21:22:01 djm Exp $ */
2/*
3 * Copyright (c) 2019 Google LLC
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * This is a tiny program used to isolate the address space used for
20 * security key middleware signing operations from ssh-agent. It is similar
21 * to ssh-pkcs11-helper.c but considerably simpler as the signing operation
22 * for this case are stateless.
23 *
24 * It receives a signing request (key, provider, message, flags) from
25 * stdin, attempts to perform a signature using the security key provider
26 * and returns the resultant signature via stdout.
27 *
28 * In the future, this program might gain additional functions to support
29 * FIDO2 tokens such as enumerating resident keys. When this happens it will
30 * be necessary to crank SSH_SK_HELPER_VERSION below.
31 */
32
33#include "includes.h"
34
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <errno.h>
41
42#include "xmalloc.h"
43#include "log.h"
44#include "sshkey.h"
45#include "authfd.h"
46#include "misc.h"
47#include "sshbuf.h"
48#include "msg.h"
49#include "uidswap.h"
50#include "sshkey.h"
51#include "ssherr.h"
52#include "ssh-sk.h"
53
54extern char *__progname;
55
56int
57main(int argc, char **argv)
58{
59 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
60 LogLevel log_level = SYSLOG_LEVEL_ERROR;
61 struct sshbuf *req, *resp, *kbuf;
62 struct sshkey *key;
63 uint32_t compat;
64 const u_char *message;
65 u_char version, *sig;
66 size_t msglen, siglen;
67 char *provider;
68 int in, out, ch, r, log_stderr = 0;
69
70 sanitise_stdfd();
71 log_init(__progname, log_level, log_facility, log_stderr);
72
73 while ((ch = getopt(argc, argv, "v")) != -1) {
74 switch (ch) {
75 case 'v':
76 log_stderr = 1;
77 if (log_level == SYSLOG_LEVEL_ERROR)
78 log_level = SYSLOG_LEVEL_DEBUG1;
79 else if (log_level < SYSLOG_LEVEL_DEBUG3)
80 log_level++;
81 break;
82 default:
83 fprintf(stderr, "usage: %s [-v]\n", __progname);
84 exit(1);
85 }
86 }
87 log_init(__progname, log_level, log_facility, log_stderr);
88
89 /*
90 * Rearrange our file descriptors a little; we don't trust the
91 * providers not to fiddle with stdin/out.
92 */
93 closefrom(STDERR_FILENO + 1);
94 if ((in = dup(STDIN_FILENO)) == -1 || (out = dup(STDOUT_FILENO)) == -1)
95 fatal("%s: dup: %s", __progname, strerror(errno));
96 close(STDIN_FILENO);
97 close(STDOUT_FILENO);
98 sanitise_stdfd(); /* resets to /dev/null */
99
100 if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL)
101 fatal("%s: sshbuf_new failed", __progname);
102 if (ssh_msg_recv(in, req) < 0)
103 fatal("ssh_msg_recv failed");
104 close(in);
105 debug("%s: received message len %zu", __progname, sshbuf_len(req));
106
107 if ((r = sshbuf_get_u8(req, &version)) != 0)
108 fatal("%s: buffer error: %s", __progname, ssh_err(r));
109 if (version != SSH_SK_HELPER_VERSION) {
110 fatal("unsupported version: received %d, expected %d",
111 version, SSH_SK_HELPER_VERSION);
112 }
113 if ((r = sshbuf_froms(req, &kbuf)) != 0 ||
114 (r = sshkey_private_deserialize(kbuf, &key)) != 0)
115 fatal("Unable to parse key: %s", ssh_err(r));
116 if (sshkey_type_plain(key->type) != KEY_ECDSA_SK)
117 fatal("Unsupported key type %s", sshkey_ssh_name(key));
118
119 if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
120 (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 ||
121 (r = sshbuf_get_u32(req, &compat)) != 0)
122 fatal("%s: buffer error: %s", __progname, ssh_err(r));
123 if (sshbuf_len(req) != 0)
124 fatal("%s: trailing data in request", __progname);
125
126 debug("%s: ready to sign with key %s, provider %s: "
127 "msg len %zu, compat 0x%lx", __progname, sshkey_type(key),
128 provider, msglen, (u_long)compat);
129
130 if ((r = sshsk_ecdsa_sign(provider, key, &sig, &siglen,
131 message, msglen, compat)) != 0)
132 fatal("Signing failed: %s", ssh_err(r));
133
134 /* send reply */
135 if ((r = sshbuf_put_string(resp, sig, siglen)) != 0)
136 fatal("%s: buffer error: %s", __progname, ssh_err(r));
137 debug("%s: reply len %zu", __progname, sshbuf_len(resp));
138 if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1)
139 fatal("ssh_msg_send failed");
140 close(out);
141
142 return (0);
143}
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 @@
1/* $OpenBSD: ssh-sk.h,v 1.1 2019/10/31 21:16:20 djm Exp $ */ 1/* $OpenBSD: ssh-sk.h,v 1.2 2019/10/31 21:22:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2019 Google LLC 3 * Copyright (c) 2019 Google LLC
4 * 4 *
@@ -21,6 +21,9 @@
21struct sshbuf; 21struct sshbuf;
22struct sshkey; 22struct sshkey;
23 23
24/* Version of protocol between ssh-agent and ssh-sk-helper */
25#define SSH_SK_HELPER_VERSION 1
26
24/* 27/*
25 * Enroll (generate) a new security-key hosted private key via the specified 28 * Enroll (generate) a new security-key hosted private key via the specified
26 * provider middleware. 29 * provider middleware.