upstream: use ssh-sk-helper for all security key signing operations

This extracts and refactors the client interface for ssh-sk-helper
from ssh-agent and generalises it for use by the other programs.
This means that most OpenSSH tools no longer need to link against
libfido2 or directly interact with /dev/uhid*

requested by, feedback and ok markus@

OpenBSD-Commit-ID: 1abcd3aea9a7460eccfbf8ca154cdfa62f1dc93f
This commit is contained in:
djm@openbsd.org 2019-12-13 19:09:10 +00:00 committed by Damien Miller
parent c33d46868c
commit b52ec0ba39
5 changed files with 33 additions and 158 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.250 2019/11/19 16:02:32 jmc Exp $ */
/* $OpenBSD: ssh-agent.c,v 1.251 2019/12/13 19:09:10 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -90,7 +90,7 @@
#include "ssherr.h"
#include "pathnames.h"
#include "ssh-pkcs11.h"
#include "ssh-sk.h"
#include "sk-api.h"
#ifndef DEFAULT_PROVIDER_WHITELIST
# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
@ -282,130 +282,6 @@ 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, *fp = NULL;
struct sshbuf *kbuf, *req, *resp;
u_char version;
struct notifier_ctx *notifier = NULL;
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 ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
SSH_FP_DEFAULT)) == NULL)
fatal("%s: sshkey_fingerprint failed", __func__);
notifier = notify_start(0,
"Confirm user presence for key %s %s", sshkey_type(key), fp);
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));
}
notify_complete(notifier);
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)
@ -415,9 +291,11 @@ process_sign_request2(SocketEntry *e)
size_t dlen, slen = 0;
u_int compat = 0, flags;
int r, ok = -1;
char *fp = NULL;
struct sshbuf *msg;
struct sshkey *key = NULL;
struct identity *id;
struct notifier_ctx *notifier = NULL;
if ((msg = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new failed", __func__);
@ -436,25 +314,27 @@ process_sign_request2(SocketEntry *e)
verbose("%s: user refused key", __func__);
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: sign: %s", __func__, ssh_err(r));
goto send;
}
} else {
if ((r = sshkey_sign(id->key, &signature, &slen,
data, dlen, agent_decode_alg(key, flags),
NULL, compat)) != 0) {
error("%s: sshkey_sign: %s", __func__, ssh_err(r));
goto send;
}
if (sshkey_is_sk(id->key) &&
(id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
SSH_FP_DEFAULT)) == NULL)
fatal("%s: fingerprint failed", __func__);
notifier = notify_start(0,
"Confirm user presence for key %s %s",
sshkey_type(id->key), fp);
}
if ((r = sshkey_sign(id->key, &signature, &slen,
data, dlen, agent_decode_alg(key, flags),
id->sk_provider, compat)) != 0) {
error("%s: sshkey_sign: %s", __func__, ssh_err(r));
goto send;
}
/* Success */
ok = 0;
send:
notify_complete(notifier);
sshkey_free(key);
free(fp);
if (ok == 0) {
if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
(r = sshbuf_put_string(msg, signature, slen)) != 0)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-sk.c,v 1.17 2019/11/27 22:32:11 djm Exp $ */
/* $OpenBSD: ssh-sk.c,v 1.18 2019/12/13 19:09:10 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
@ -464,7 +464,7 @@ sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
}
int
sshsk_sign(const char *provider_path, const struct sshkey *key,
sshsk_sign(const char *provider_path, struct sshkey *key,
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
u_int compat)
{

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-sk.h,v 1.5 2019/11/12 19:31:45 markus Exp $ */
/* $OpenBSD: ssh-sk.h,v 1.6 2019/12/13 19:09:10 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
@ -21,9 +21,6 @@
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 of given type
* via the specified provider middleware.
@ -44,7 +41,7 @@ int sshsk_enroll(int type, const char *provider_path, const char *application,
*
* Returns 0 on success or a ssherr.h error code on failure.
*/
int sshsk_sign(const char *provider_path, const struct sshkey *key,
int sshsk_sign(const char *provider_path, struct sshkey *key,
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
u_int compat);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.96 2019/11/25 00:51:37 djm Exp $ */
/* $OpenBSD: sshkey.c,v 1.97 2019/12/13 19:09:10 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -2750,13 +2750,6 @@ sshkey_sign(struct sshkey *key,
case KEY_ECDSA:
r = ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
break;
# ifdef ENABLE_SK
case KEY_ECDSA_SK_CERT:
case KEY_ECDSA_SK:
r = sshsk_sign(sk_provider, key, sigp, lenp, data, datalen,
compat);
break;
# endif /* ENABLE_SK */
# endif /* OPENSSL_HAS_ECC */
case KEY_RSA_CERT:
case KEY_RSA:
@ -2770,8 +2763,10 @@ sshkey_sign(struct sshkey *key,
#ifdef ENABLE_SK
case KEY_ED25519_SK:
case KEY_ED25519_SK_CERT:
r = sshsk_sign(sk_provider, key, sigp, lenp, data, datalen,
compat);
case KEY_ECDSA_SK_CERT:
case KEY_ECDSA_SK:
r = sshsk_sign(sk_provider, key, sigp, lenp, data,
datalen, compat);
break;
#endif /* ENABLE_SK */
#ifdef WITH_XMSS

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.40 2019/11/25 00:51:37 djm Exp $ */
/* $OpenBSD: sshkey.h,v 1.41 2019/12/13 19:09:10 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -51,6 +51,9 @@
#define SSH_RSA_MINIMUM_MODULUS_SIZE 1024
#define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20)
/* Version of protocol expected from ssh-sk-helper */
#define SSH_SK_HELPER_VERSION 1
struct sshbuf;
/* Key types */