diff options
-rw-r--r-- | Makefile.in | 11 | ||||
-rw-r--r-- | pathnames.h | 7 | ||||
-rw-r--r-- | ssh-agent.1 | 20 | ||||
-rw-r--r-- | ssh-agent.c | 218 | ||||
-rw-r--r-- | ssh-sk-helper.c | 143 | ||||
-rw-r--r-- | ssh-sk.h | 5 |
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 | |||
24 | SFTP_SERVER=$(libexecdir)/sftp-server | 24 | SFTP_SERVER=$(libexecdir)/sftp-server |
25 | SSH_KEYSIGN=$(libexecdir)/ssh-keysign | 25 | SSH_KEYSIGN=$(libexecdir)/ssh-keysign |
26 | SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper | 26 | SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper |
27 | SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper | ||
27 | PRIVSEP_PATH=@PRIVSEP_PATH@ | 28 | PRIVSEP_PATH=@PRIVSEP_PATH@ |
28 | SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ | 29 | SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ |
29 | STRIP_OPT=@STRIP_OPT@ | 30 | STRIP_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@ | |||
60 | MANFMT=@MANFMT@ | 62 | MANFMT=@MANFMT@ |
61 | MKDIR_P=@MKDIR_P@ | 63 | MKDIR_P=@MKDIR_P@ |
62 | 64 | ||
63 | 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) | 65 | 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) |
64 | 66 | ||
65 | XMSS_OBJS=\ | 67 | XMSS_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 | |||
199 | ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o | 201 | ssh-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 | ||
204 | ssh-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 | |||
202 | ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o | 207 | ssh-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 | |||
122 | Kill the current agent (given by the | 122 | Kill the current agent (given by the |
123 | .Ev SSH_AGENT_PID | 123 | .Ev SSH_AGENT_PID |
124 | environment variable). | 124 | environment variable). |
125 | .It Fl P Ar pkcs11_whitelist | 125 | .It Fl P Ar provider_whitelist |
126 | Specify a pattern-list of acceptable paths for PKCS#11 shared libraries | 126 | Specify a pattern-list of acceptable paths for PKCS#11 and security key shared |
127 | that may be added using the | 127 | libraries that may be used with the |
128 | .Fl s | 128 | .Fl s |
129 | option to | 129 | or |
130 | .Fl S | ||
131 | options to | ||
130 | .Xr ssh-add 1 . | 132 | .Xr ssh-add 1 . |
131 | The default is to allow loading PKCS#11 libraries from | 133 | The default is to allow loading libraries from |
132 | .Dq /usr/lib/*,/usr/local/lib/* . | 134 | .Dq /usr/lib/*,/usr/local/lib/* . |
133 | PKCS#11 libraries that do not match the whitelist will be refused. | 135 | Libraries that do not match the whitelist will be refused. |
134 | See PATTERNS in | 136 | See PATTERNS in |
135 | .Xr ssh_config 5 | 137 | .Xr ssh_config 5 |
136 | for a description of pattern-list syntax. | 138 | 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 @@ | |||
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 | ||
128 | struct idtable { | 130 | struct idtable { |
@@ -146,8 +148,8 @@ pid_t cleanup_pid = 0; | |||
146 | char socket_name[PATH_MAX]; | 148 | char socket_name[PATH_MAX]; |
147 | char socket_dir[PATH_MAX]; | 149 | char socket_dir[PATH_MAX]; |
148 | 150 | ||
149 | /* PKCS#11 path whitelist */ | 151 | /* PKCS#11/Security key path whitelist */ |
150 | static char *pkcs11_whitelist; | 152 | static 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 | ||
284 | static int | ||
285 | provider_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 */ |
282 | static void | 400 | static void |
283 | process_sign_request2(SocketEntry *e) | 401 | process_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); | ||
486 | send: | 666 | send: |
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 | |||
54 | extern char *__progname; | ||
55 | |||
56 | int | ||
57 | main(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 | } | ||
@@ -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 @@ | |||
21 | struct sshbuf; | 21 | struct sshbuf; |
22 | struct sshkey; | 22 | struct 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. |