upstream commit

Allow ssh-keygen to use a key held in ssh-agent as a CA when
signing certificates. bz#2377 ok markus

Upstream-ID: fb42e920b592edcbb5b50465739a867c09329c8f
This commit is contained in:
djm@openbsd.org 2017-06-28 01:09:22 +00:00 committed by Damien Miller
parent c9cdef3552
commit a98339edbc
6 changed files with 121 additions and 22 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.c,v 1.103 2017/05/05 10:42:49 naddy Exp $ */ /* $OpenBSD: authfd.c,v 1.104 2017/06/28 01:09:22 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
@ -326,7 +326,7 @@ ssh_free_identitylist(struct ssh_identitylist *idl)
/* encode signature algoritm in flag bits, so we can keep the msg format */ /* encode signature algoritm in flag bits, so we can keep the msg format */
static u_int static u_int
agent_encode_alg(struct sshkey *key, const char *alg) agent_encode_alg(const struct sshkey *key, const char *alg)
{ {
if (alg != NULL && key->type == KEY_RSA) { if (alg != NULL && key->type == KEY_RSA) {
if (strcmp(alg, "rsa-sha2-256") == 0) if (strcmp(alg, "rsa-sha2-256") == 0)
@ -339,7 +339,7 @@ agent_encode_alg(struct sshkey *key, const char *alg)
/* ask agent to sign data, returns err.h code on error, 0 on success */ /* ask agent to sign data, returns err.h code on error, 0 on success */
int int
ssh_agent_sign(int sock, struct sshkey *key, ssh_agent_sign(int sock, const struct sshkey *key,
u_char **sigp, size_t *lenp, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const char *alg, u_int compat) const u_char *data, size_t datalen, const char *alg, u_int compat)
{ {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.h,v 1.40 2017/05/05 10:42:49 naddy Exp $ */ /* $OpenBSD: authfd.h,v 1.41 2017/06/28 01:09:22 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -38,7 +38,7 @@ int ssh_remove_all_identities(int sock, int version);
int ssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge, int ssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge,
u_char session_id[16], u_char response[16]); u_char session_id[16], u_char response[16]);
int ssh_agent_sign(int sock, struct sshkey *key, int ssh_agent_sign(int sock, const struct sshkey *key,
u_char **sigp, size_t *lenp, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const char *alg, u_int compat); const u_char *data, size_t datalen, const char *alg, u_int compat);

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.141 2017/05/05 10:41:58 naddy Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.142 2017/06/28 01:09:22 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
@ -35,7 +35,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: May 5 2017 $ .Dd $Mdocdate: June 28 2017 $
.Dt SSH-KEYGEN 1 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -114,6 +114,8 @@
.Fl s Ar ca_key .Fl s Ar ca_key
.Fl I Ar certificate_identity .Fl I Ar certificate_identity
.Op Fl h .Op Fl h
.Op Fl U
.Op Fl D Ar pkcs11_provider
.Op Fl n Ar principals .Op Fl n Ar principals
.Op Fl O Ar option .Op Fl O Ar option
.Op Fl V Ar validity_interval .Op Fl V Ar validity_interval
@ -558,6 +560,14 @@ The possible values are
.Dq ed25519 , .Dq ed25519 ,
or or
.Dq rsa . .Dq rsa .
.It Fl U
When used in combination with
.Fl s ,
this option indicates that a CA key resides in a
.Xr ssh-agent 1 .
See the
.Sx CERTIFICATES
section for more information.
.It Fl u .It Fl u
Update a KRL. Update a KRL.
When specified with When specified with
@ -705,6 +715,14 @@ to
.Pp .Pp
.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub .Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub
.Pp .Pp
Similarly, it is possible for the CA key to be hosted in a
.Xr ssh-agent 1 .
This is indicated by the
.Fl U
flag and, again, the CA key must be identified by its public half.
.Pp
.Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub
.Pp
In all cases, In all cases,
.Ar key_id .Ar key_id
is a "key identifier" that is logged by the server when the certificate is a "key identifier" that is logged by the server when the certificate

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.304 2017/05/30 14:16:41 markus Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.305 2017/06/28 01:09:22 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -59,6 +59,7 @@
#include "krl.h" #include "krl.h"
#include "digest.h" #include "digest.h"
#include "utf8.h" #include "utf8.h"
#include "authfd.h"
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL
# define DEFAULT_KEY_TYPE_NAME "rsa" # define DEFAULT_KEY_TYPE_NAME "rsa"
@ -121,6 +122,9 @@ char *identity_comment = NULL;
/* Path to CA key when certifying keys. */ /* Path to CA key when certifying keys. */
char *ca_key_path = NULL; char *ca_key_path = NULL;
/* Prefer to use agent keys for CA signing */
int prefer_agent = 0;
/* Certificate serial number */ /* Certificate serial number */
unsigned long long cert_serial = 0; unsigned long long cert_serial = 0;
@ -1597,24 +1601,66 @@ load_pkcs11_key(char *path)
#endif /* ENABLE_PKCS11 */ #endif /* ENABLE_PKCS11 */
} }
/* Signer for sshkey_certify_custom that uses the agent */
static int
agent_signer(const struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen,
const char *alg, u_int compat, void *ctx)
{
int *agent_fdp = (int *)ctx;
return ssh_agent_sign(*agent_fdp, key, sigp, lenp,
data, datalen, alg, compat);
}
static void static void
do_ca_sign(struct passwd *pw, int argc, char **argv) do_ca_sign(struct passwd *pw, int argc, char **argv)
{ {
int r, i, fd; int r, i, fd, found, agent_fd = -1;
u_int n; u_int n;
struct sshkey *ca, *public; struct sshkey *ca, *public;
char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
FILE *f; FILE *f;
struct ssh_identitylist *agent_ids;
size_t j;
#ifdef ENABLE_PKCS11 #ifdef ENABLE_PKCS11
pkcs11_init(1); pkcs11_init(1);
#endif #endif
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
if (pkcs11provider != NULL) { if (pkcs11provider != NULL) {
/* If a PKCS#11 token was specified then try to use it */
if ((ca = load_pkcs11_key(tmp)) == NULL) if ((ca = load_pkcs11_key(tmp)) == NULL)
fatal("No PKCS#11 key matching %s found", ca_key_path); fatal("No PKCS#11 key matching %s found", ca_key_path);
} else } else if (prefer_agent) {
/*
* Agent signature requested. Try to use agent after making
* sure the public key specified is actually present in the
* agent.
*/
if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
fatal("Cannot load CA public key %s: %s",
tmp, ssh_err(r));
if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
fatal("Cannot use public key for CA signature: %s",
ssh_err(r));
if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0)
fatal("Retrieve agent key list: %s", ssh_err(r));
found = 0;
for (j = 0; j < agent_ids->nkeys; j++) {
if (sshkey_equal(ca, agent_ids->keys[j])) {
found = 1;
break;
}
}
if (!found)
fatal("CA key %s not found in agent", tmp);
ssh_free_identitylist(agent_ids);
ca->flags |= SSHKEY_FLAG_EXT;
} else {
/* CA key is assumed to be a private key on the filesystem */
ca = load_identity(tmp); ca = load_identity(tmp);
}
free(tmp); free(tmp);
if (key_type_name != NULL && if (key_type_name != NULL &&
@ -1664,8 +1710,16 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
&public->cert->signature_key)) != 0) &public->cert->signature_key)) != 0)
fatal("sshkey_from_private (ca key): %s", ssh_err(r)); fatal("sshkey_from_private (ca key): %s", ssh_err(r));
if ((r = sshkey_certify(public, ca, key_type_name)) != 0) if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
fatal("Couldn't certify key %s: %s", tmp, ssh_err(r)); if ((r = sshkey_certify_custom(public, ca,
key_type_name, agent_signer, &agent_fd)) != 0)
fatal("Couldn't certify key %s via agent: %s",
tmp, ssh_err(r));
} else {
if ((sshkey_certify(public, ca, key_type_name)) != 0)
fatal("Couldn't certify key %s: %s",
tmp, ssh_err(r));
}
if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
*cp = '\0'; *cp = '\0';
@ -2261,8 +2315,9 @@ usage(void)
" ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
" [-j start_line] [-K checkpt] [-W generator]\n" " [-j start_line] [-K checkpt] [-W generator]\n"
#endif #endif
" ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n" " ssh-keygen -s ca_key -I certificate_identity [-h] [-U]\n"
" [-O option] [-V validity_interval] [-z serial_number] file ...\n" " [-D pkcs11_provider] [-n principals] [-O option]\n"
" [-V validity_interval] [-z serial_number] file ...\n"
" ssh-keygen -L [-f input_keyfile]\n" " ssh-keygen -L [-f input_keyfile]\n"
" ssh-keygen -A\n" " ssh-keygen -A\n"
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
@ -2320,8 +2375,8 @@ main(int argc, char **argv)
if (gethostname(hostname, sizeof(hostname)) < 0) if (gethostname(hostname, sizeof(hostname)) < 0)
fatal("gethostname: %s", strerror(errno)); fatal("gethostname: %s", strerror(errno));
/* Remaining characters: UYdw */ /* Remaining characters: Ydw */
while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy" while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy"
"C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:"
"a:b:f:g:j:m:n:r:s:t:z:")) != -1) { "a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
switch (opt) { switch (opt) {
@ -2448,6 +2503,9 @@ main(int argc, char **argv)
case 'D': case 'D':
pkcs11provider = optarg; pkcs11provider = optarg;
break; break;
case 'U':
prefer_agent = 1;
break;
case 'u': case 'u':
update_krl = 1; update_krl = 1;
break; break;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.52 2017/06/09 06:40:24 djm Exp $ */ /* $OpenBSD: sshkey.c,v 1.53 2017/06/28 01:09:22 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -2253,7 +2253,8 @@ sshkey_drop_cert(struct sshkey *k)
/* Sign a certified key, (re-)generating the signed certblob. */ /* Sign a certified key, (re-)generating the signed certblob. */
int int
sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
sshkey_certify_signer *signer, void *signer_ctx)
{ {
struct sshbuf *principals = NULL; struct sshbuf *principals = NULL;
u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
@ -2342,8 +2343,8 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
goto out; goto out;
/* Sign the whole mess */ /* Sign the whole mess */
if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
sshbuf_len(cert), alg, 0)) != 0) sshbuf_len(cert), alg, 0, signer_ctx)) != 0)
goto out; goto out;
/* Append signature and we are done */ /* Append signature and we are done */
@ -2359,6 +2360,22 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
return ret; return ret;
} }
static int
default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen,
const char *alg, u_int compat, void *ctx)
{
if (ctx != NULL)
return SSH_ERR_INVALID_ARGUMENT;
return sshkey_sign(key, sigp, lenp, data, datalen, alg, compat);
}
int
sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
{
return sshkey_certify_custom(k, ca, alg, default_key_sign, NULL);
}
int int
sshkey_cert_check_authority(const struct sshkey *k, sshkey_cert_check_authority(const struct sshkey *k,
int want_host, int require_principal, int want_host, int require_principal,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.19 2017/06/13 11:22:15 djm Exp $ */ /* $OpenBSD: sshkey.h,v 1.20 2017/06/28 01:09:22 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -137,13 +137,19 @@ int sshkey_type_is_cert(int);
int sshkey_type_plain(int); int sshkey_type_plain(int);
int sshkey_to_certified(struct sshkey *); int sshkey_to_certified(struct sshkey *);
int sshkey_drop_cert(struct sshkey *); int sshkey_drop_cert(struct sshkey *);
int sshkey_certify(struct sshkey *, struct sshkey *, const char *);
int sshkey_cert_copy(const struct sshkey *, struct sshkey *); int sshkey_cert_copy(const struct sshkey *, struct sshkey *);
int sshkey_cert_check_authority(const struct sshkey *, int, int, int sshkey_cert_check_authority(const struct sshkey *, int, int,
const char *, const char **); const char *, const char **);
size_t sshkey_format_cert_validity(const struct sshkey_cert *, size_t sshkey_format_cert_validity(const struct sshkey_cert *,
char *, size_t) __attribute__((__bounded__(__string__, 2, 3))); char *, size_t) __attribute__((__bounded__(__string__, 2, 3)));
int sshkey_certify(struct sshkey *, struct sshkey *, const char *);
/* Variant allowing use of a custom signature function (e.g. for ssh-agent) */
typedef int sshkey_certify_signer(const struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, u_int, void *);
int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *,
sshkey_certify_signer *, void *);
int sshkey_ecdsa_nid_from_name(const char *); int sshkey_ecdsa_nid_from_name(const char *);
int sshkey_curve_name_to_nid(const char *); int sshkey_curve_name_to_nid(const char *);
const char * sshkey_curve_nid_to_name(int); const char * sshkey_curve_nid_to_name(int);