Meghana Bhat
2015-Aug-28 20:29 UTC
[PATCH] ssh-keygen: Add ssh agent support for generating certificates
Communicate with the ssh agent to sign certificates if agent is present and is loaded with the specified certificate authority. In order for the agent to sign, the certificate authority public key must be alongside the private key with the same file name but a .pub extension. This patch addresses the bug at: https://bugzilla.mindrot.org/show_bug.cgi?id=2377 Patch developed against 7.1p. --- authfd.c | 2 +- authfd.h | 1 + ssh-keygen.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- sshkey.c | 36 +++++++++++++++----- sshkey.h | 1 + 5 files changed, 126 insertions(+), 23 deletions(-) diff --git a/authfd.c b/authfd.c index eaa1426..f7933fc 100644 --- a/authfd.c +++ b/authfd.c @@ -121,7 +121,7 @@ ssh_get_authentication_socket(int *fdp) } /* Communicate with agent: send request and read reply */ -static int +int ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) { int r; diff --git a/authfd.h b/authfd.h index bea20c2..f58af43 100644 --- a/authfd.h +++ b/authfd.h @@ -42,6 +42,7 @@ int ssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge, int ssh_agent_sign(int sock, struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); +int ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply); /* Messages for the authentication agent connection. */ #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 diff --git a/ssh-keygen.c b/ssh-keygen.c index 4e0a855..168d6c0 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -57,6 +57,7 @@ #include "atomicio.h" #include "krl.h" #include "digest.h" +#include "authfd.h" #ifdef WITH_OPENSSL # define DEFAULT_KEY_TYPE_NAME "rsa" @@ -1566,25 +1567,92 @@ load_pkcs11_key(char *path) #endif /* ENABLE_PKCS11 */ } +static int +do_agent_sign(int agent_fd, struct sshkey *k, struct sshkey *ca_pk, + u_char *ca_blob, size_t ca_len) +{ + u_char type; + u_char *sig; + size_t slen; + struct sshbuf *msg, *cert_blob; + u_int flags = 0; + int ret = 0, r = 0; + + cert_blob = k->cert->certblob; /* for readability */ + if ((msg = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshkey_cert_prepare_sign(k, ca_pk)) != 0) { + ret = -1; + } + + if (ret == 0) { + if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || + (r = sshbuf_put_string(msg, ca_blob, ca_len)) != 0 || + (r = sshbuf_put_string(msg, sshbuf_ptr(cert_blob), + sshbuf_len(cert_blob))) != 0 || + (r = sshbuf_put_u32(msg, flags)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if ((r = ssh_request_reply(agent_fd, msg, msg)) != 0) + ret = -1; + else if ((r = sshbuf_get_u8(msg, &type)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + else if ((type == SSH_AGENT_FAILURE) || + (type == SSH2_AGENT_FAILURE)) + ret = -1; + else if ((r = sshbuf_get_string(msg, &sig, &slen)) != 0 || + (r = sshbuf_put_string(cert_blob, sig, slen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + else + free(sig); + } + + sshbuf_free(msg); + return ret; +} + static void do_ca_sign(struct passwd *pw, int argc, char **argv) { - int r, i, fd; + int r, i, fd, agent_fd; u_int n; - struct sshkey *ca, *public; + struct sshkey *ca, *ca_pk, *public; char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; + u_char *ca_blob; + size_t ca_len; + /* flag indicating whether to try the ssh-agent to sign certificates */ + int try_agent = 0; #ifdef ENABLE_PKCS11 pkcs11_init(1); #endif + + /* load pubkey of CA first (ca_blob), if it works, try getting agent socket */ tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); - if (pkcs11provider != NULL) { - if ((ca = load_pkcs11_key(tmp)) == NULL) - fatal("No PKCS#11 key matching %s found", ca_key_path); - } else - ca = load_identity(tmp); - free(tmp); + if ((r = sshkey_load_public(tmp, &ca_pk, NULL)) == 0 && + (r = sshkey_to_blob(ca_pk, &ca_blob, &ca_len)) == 0) { + switch (r = ssh_get_authentication_socket(&agent_fd)) { + case SSH_ERR_SUCCESS: + try_agent = 1; + ca = NULL; + break; + case SSH_ERR_AGENT_NOT_PRESENT: + debug("Couldn't open connection to agent"); + break; + default: + debug("Error connecting to agent"); + break; + } + } + + if (!try_agent) { + if (pkcs11provider != NULL) { + if ((ca = load_pkcs11_key(tmp)) == NULL) + fatal("No PKCS#11 key matching %s found", ca_key_path); + } else + ca = load_identity(tmp); + free(tmp); + } for (i = 0; i < argc; i++) { /* Split list of principals */ @@ -1623,13 +1691,28 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); - if ((r = sshkey_from_private(ca, - &public->cert->signature_key)) != 0) - fatal("key_from_private (ca key): %s", ssh_err(r)); - if (sshkey_certify(public, ca) != 0) - fatal("Couldn't not certify key %s", tmp); + if (try_agent && + (r = do_agent_sign(agent_fd, public, ca_pk, ca_blob, ca_len)) != 0) { + try_agent = 0; + otmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if (pkcs11provider != NULL) { + if ((ca = load_pkcs11_key(otmp)) == NULL) + fatal("No PKCS#11 key matching %s found", ca_key_path); + } else + ca = load_identity(otmp); + free(otmp); + } + if (!try_agent) { + if ((r = sshkey_from_private(ca, + &public->cert->signature_key)) != 0) + fatal("key_from_private (ca key): %s", ssh_err(r)); + + if (sshkey_certify(public, ca) != 0) + fatal("Couldn't not certify key %s", tmp); + } + if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) *cp = '\0'; xasprintf(&out, "%s-cert.pub", tmp); diff --git a/sshkey.c b/sshkey.c index 32dd8f2..bded18a 100644 --- a/sshkey.c +++ b/sshkey.c @@ -2370,13 +2370,12 @@ sshkey_drop_cert(struct sshkey *k) return 0; } -/* Sign a certified key, (re-)generating the signed certblob. */ +/* Prepare a certificate blob for CA signing. */ int -sshkey_certify(struct sshkey *k, struct sshkey *ca) -{ +sshkey_cert_prepare_sign(struct sshkey *k, struct sshkey *ca) { struct sshbuf *principals = NULL; - u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; - size_t i, ca_len, sig_len; + u_char *ca_blob = NULL, nonce[32]; + size_t i, ca_len; int ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *cert; @@ -2459,7 +2458,30 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca) (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) goto out; + ret = 0; + out: + if (ret != 0) + sshbuf_reset(cert); + if (ca_blob != NULL) + free(ca_blob); + if (principals != NULL) + sshbuf_free(principals); + return ret; +} +/* Sign a certified key, (re-)generating the signed certblob. */ +int +sshkey_certify(struct sshkey *k, struct sshkey *ca) +{ + u_char *sig_blob = NULL; + size_t sig_len; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *cert; + + cert = k->cert->certblob; /* for readability */ + if ((ret = sshkey_cert_prepare_sign(k, ca)) != 0) + goto out; + /* Sign the whole mess */ if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), sshbuf_len(cert), 0)) != 0) @@ -2474,10 +2496,6 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca) sshbuf_reset(cert); if (sig_blob != NULL) free(sig_blob); - if (ca_blob != NULL) - free(ca_blob); - if (principals != NULL) - sshbuf_free(principals); return ret; } diff --git a/sshkey.h b/sshkey.h index c8d3cdd..0a62563 100644 --- a/sshkey.h +++ b/sshkey.h @@ -137,6 +137,7 @@ int sshkey_type_is_cert(int); int sshkey_type_plain(int); int sshkey_to_certified(struct sshkey *); int sshkey_drop_cert(struct sshkey *); +int sshkey_cert_prepare_sign(struct sshkey *, struct sshkey *); int sshkey_certify(struct sshkey *, struct sshkey *); int sshkey_cert_copy(const struct sshkey *, struct sshkey *); int sshkey_cert_check_authority(const struct sshkey *, int, int, -- 2.3.2 (Apple Git-55)