upstream: ssh-agent support for U2F/FIDO keys

feedback & ok markus@

OpenBSD-Commit-ID: bb544a44bc32e45d2ec8bf652db2046f38360acb
This commit is contained in:
djm@openbsd.org 2019-10-31 21:22:01 +00:00 committed by Damien Miller
parent eebec620c9
commit 07da39f71d
6 changed files with 372 additions and 32 deletions

View File

@ -24,6 +24,7 @@ ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass
SFTP_SERVER=$(libexecdir)/sftp-server SFTP_SERVER=$(libexecdir)/sftp-server
SSH_KEYSIGN=$(libexecdir)/ssh-keysign SSH_KEYSIGN=$(libexecdir)/ssh-keysign
SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper
PRIVSEP_PATH=@PRIVSEP_PATH@ PRIVSEP_PATH=@PRIVSEP_PATH@
SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
STRIP_OPT=@STRIP_OPT@ STRIP_OPT=@STRIP_OPT@
@ -35,6 +36,7 @@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \
-D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \
-D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \
-D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
-D_PATH_SSH_SK_HELPER=\"$(SSH_SK_HELPER)\" \
-D_PATH_SSH_PIDDIR=\"$(piddir)\" \ -D_PATH_SSH_PIDDIR=\"$(piddir)\" \
-D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\"
@ -60,7 +62,7 @@ EXEEXT=@EXEEXT@
MANFMT=@MANFMT@ MANFMT=@MANFMT@
MKDIR_P=@MKDIR_P@ 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=\ XMSS_OBJS=\
ssh-xmss.o \ 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 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) $(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 ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o
$(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) $(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 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
$(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(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-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$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT)
$(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(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 $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
@ -426,6 +432,7 @@ uninstall:
-rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
-rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
-rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(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/ssh.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.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_SSHADD="$${BUILDDIR}/ssh-add"; \
TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \ TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \
TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
TEST_SSH_SSHSKHELPER="$${BUILDDIR}/ssh-sk-helper"; \
TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \ TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \
TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \ TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \
TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \ TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \
TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
TEST_SSH_PLINK="plink"; \ TEST_SSH_PLINK="plink"; \
TEST_SSH_PUTTYGEN="puttygen"; \ TEST_SSH_PUTTYGEN="puttygen"; \
TEST_SSH_CONCH="conch"; \ TEST_SSH_CONCH="conch"; \

View File

@ -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@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -133,6 +133,11 @@
#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" #define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper"
#endif #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 */ /* xauth for X11 forwarding */
#ifndef _PATH_XAUTH #ifndef _PATH_XAUTH
#define _PATH_XAUTH "/usr/X11R6/bin/xauth" #define _PATH_XAUTH "/usr/X11R6/bin/xauth"

View File

@ -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@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 .Dt SSH-AGENT 1
.Os .Os
.Sh NAME .Sh NAME
@ -46,7 +46,7 @@
.Op Fl \&Dd .Op Fl \&Dd
.Op Fl a Ar bind_address .Op Fl a Ar bind_address
.Op Fl E Ar fingerprint_hash .Op Fl E Ar fingerprint_hash
.Op Fl P Ar pkcs11_whitelist .Op Fl P Ar provider_whitelist
.Op Fl t Ar life .Op Fl t Ar life
.Op Ar command Op Ar arg ... .Op Ar command Op Ar arg ...
.Nm ssh-agent .Nm ssh-agent
@ -122,15 +122,17 @@ The default is
Kill the current agent (given by the Kill the current agent (given by the
.Ev SSH_AGENT_PID .Ev SSH_AGENT_PID
environment variable). environment variable).
.It Fl P Ar pkcs11_whitelist .It Fl P Ar provider_whitelist
Specify a pattern-list of acceptable paths for PKCS#11 shared libraries Specify a pattern-list of acceptable paths for PKCS#11 and security key shared
that may be added using the libraries that may be used with the
.Fl s .Fl s
option to or
.Fl S
options to
.Xr ssh-add 1 . .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/* . .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 See PATTERNS in
.Xr ssh_config 5 .Xr ssh_config 5
for a description of pattern-list syntax. for a description of pattern-list syntax.

View File

@ -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@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -41,6 +41,7 @@
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h>
#ifdef HAVE_SYS_TIME_H #ifdef HAVE_SYS_TIME_H
# include <sys/time.h> # include <sys/time.h>
#endif #endif
@ -85,13 +86,13 @@
#include "digest.h" #include "digest.h"
#include "ssherr.h" #include "ssherr.h"
#include "match.h" #include "match.h"
#include "msg.h"
#ifdef ENABLE_PKCS11 #include "pathnames.h"
#include "ssh-pkcs11.h" #include "ssh-pkcs11.h"
#endif #include "ssh-sk.h"
#ifndef DEFAULT_PKCS11_WHITELIST #ifndef DEFAULT_PROVIDER_WHITELIST
# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" # define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
#endif #endif
/* Maximum accepted message length */ /* Maximum accepted message length */
@ -123,6 +124,7 @@ typedef struct identity {
char *provider; char *provider;
time_t death; time_t death;
u_int confirm; u_int confirm;
char *sk_provider;
} Identity; } Identity;
struct idtable { struct idtable {
@ -146,8 +148,8 @@ pid_t cleanup_pid = 0;
char socket_name[PATH_MAX]; char socket_name[PATH_MAX];
char socket_dir[PATH_MAX]; char socket_dir[PATH_MAX];
/* PKCS#11 path whitelist */ /* PKCS#11/Security key path whitelist */
static char *pkcs11_whitelist; static char *provider_whitelist;
/* locking */ /* locking */
#define LOCK_SIZE 32 #define LOCK_SIZE 32
@ -189,6 +191,7 @@ free_identity(Identity *id)
sshkey_free(id->key); sshkey_free(id->key);
free(id->provider); free(id->provider);
free(id->comment); free(id->comment);
free(id->sk_provider);
free(id); free(id);
} }
@ -278,6 +281,121 @@ agent_decode_alg(struct sshkey *key, u_int flags)
return NULL; 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 */ /* ssh2 only */
static void static void
process_sign_request2(SocketEntry *e) process_sign_request2(SocketEntry *e)
@ -308,11 +426,20 @@ process_sign_request2(SocketEntry *e)
verbose("%s: user refused key", __func__); verbose("%s: user refused key", __func__);
goto send; 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, if ((r = sshkey_sign(id->key, &signature, &slen,
data, dlen, agent_decode_alg(key, flags), compat)) != 0) { data, dlen, agent_decode_alg(key, flags), compat)) != 0) {
error("%s: sshkey_sign: %s", __func__, ssh_err(r)); error("%s: sshkey_sign: %s", __func__, ssh_err(r));
goto send; goto send;
} }
}
/* Success */ /* Success */
ok = 0; ok = 0;
send: send:
@ -411,7 +538,7 @@ process_add_identity(SocketEntry *e)
Identity *id; Identity *id;
int success = 0, confirm = 0; int success = 0, confirm = 0;
u_int seconds, maxsign; u_int seconds, maxsign;
char *comment = NULL; char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
time_t death = 0; time_t death = 0;
struct sshkey *k = NULL; struct sshkey *k = NULL;
u_char ctype; u_char ctype;
@ -456,15 +583,58 @@ process_add_identity(SocketEntry *e)
goto err; goto err;
} }
break; 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: default:
error("%s: Unknown constraint %d", __func__, ctype); error("%s: Unknown constraint %d", __func__, ctype);
err: err:
free(sk_provider);
free(ext_name);
sshbuf_reset(e->request); sshbuf_reset(e->request);
free(comment); free(comment);
sshkey_free(k); sshkey_free(k);
goto send; 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; success = 1;
if (lifetime && !death) if (lifetime && !death)
@ -478,11 +648,21 @@ process_add_identity(SocketEntry *e)
/* key state might have been updated */ /* key state might have been updated */
sshkey_free(id->key); sshkey_free(id->key);
free(id->comment); free(id->comment);
free(id->sk_provider);
} }
id->key = k; id->key = k;
id->comment = comment; id->comment = comment;
id->death = death; id->death = death;
id->confirm = confirm; 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:
send_status(e, success); send_status(e, success);
} }
@ -600,7 +780,7 @@ process_add_smartcard_key(SocketEntry *e)
provider, strerror(errno)); provider, strerror(errno));
goto send; 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\": " verbose("refusing PKCS#11 add of \"%.100s\": "
"provider not whitelisted", canonical_provider); "provider not whitelisted", canonical_provider);
goto send; goto send;
@ -1079,7 +1259,7 @@ usage(void)
{ {
fprintf(stderr, fprintf(stderr,
"usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" "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"); " ssh-agent [-c | -s] -k\n");
exit(1); exit(1);
} }
@ -1137,9 +1317,9 @@ main(int ac, char **av)
k_flag++; k_flag++;
break; break;
case 'P': case 'P':
if (pkcs11_whitelist != NULL) if (provider_whitelist != NULL)
fatal("-P option already specified"); fatal("-P option already specified");
pkcs11_whitelist = xstrdup(optarg); provider_whitelist = xstrdup(optarg);
break; break;
case 's': case 's':
if (c_flag) 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)) if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
usage(); usage();
if (pkcs11_whitelist == NULL) if (provider_whitelist == NULL)
pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST);
if (ac == 0 && !c_flag && !s_flag) { if (ac == 0 && !c_flag && !s_flag) {
shell = getenv("SHELL"); shell = getenv("SHELL");

143
ssh-sk-helper.c Normal file
View File

@ -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);
}

View File

@ -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 * Copyright (c) 2019 Google LLC
* *
@ -21,6 +21,9 @@
struct sshbuf; struct sshbuf;
struct sshkey; 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 * Enroll (generate) a new security-key hosted private key via the specified
* provider middleware. * provider middleware.