[PATCH] ssh-keygen: Add ssh agent support for generating certificates

Meghana Bhat mebhat at akamai.com
Sat Aug 29 06:29:36 AEST 2015


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)



More information about the openssh-unix-dev mailing list