[PATCH v2 1/2] sshkey: expose openssl EVP_PKEY to sshkey conversion routine.

James Bottomley
2020-06-09T19:36:32Z

Break a new function, sshkey_openssl_private_key(), out of
sshkey_parse_private_pem_fileblob() which will be used with engine
keys that have no internal private key.  Add an external flag, which
disables any private key component checking and sets SSHKEY_FLAG_EXT
on the sshkey.

Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
---
 sshkey.c | 87 ++++++++++++++++++++++++++++++++------------------------
 sshkey.h |  5 ++++
 2 files changed, 55 insertions(+), 37 deletions(-)

diff --git a/sshkey.c b/sshkey.c
index 1571e3d93..32beb73f5 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -4519,47 +4519,18 @@ pem_passphrase_cb(char *buf, int size, int rwflag, void
*u)
 	return (int)len;
 }

-static int
-sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
-    const char *passphrase, struct sshkey **keyp)
+int
+sshkey_openssl_private_key(int type, EVP_PKEY *pk, struct sshkey **keyp,
+			   int external)
 {
-	EVP_PKEY *pk = NULL;
 	struct sshkey *prv = NULL;
-	BIO *bio = NULL;
 	int r;

-	if (keyp != NULL)
-		*keyp = NULL;
-
-	if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
-		return SSH_ERR_ALLOC_FAIL;
-	if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !-
(int)sshbuf_len(blob)) {
-		r = SSH_ERR_ALLOC_FAIL;
-		goto out;
-	}
-
-	clear_libcrypto_errors();
-	if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb,
-	    (char *)passphrase)) == NULL) {
-	       /*
-		* libcrypto may return various ASN.1 errors when attempting
-		* to parse a key with an incorrect passphrase.
-		* Treat all format errors as "incorrect passphrase" if a
-		* passphrase was supplied.
-		*/
-		if (passphrase != NULL && *passphrase != '\0')
-			r = SSH_ERR_KEY_WRONG_PASSPHRASE;
-		else
-			r = convert_libcrypto_error();
-		goto out;
-	}
 	if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA &&
 	    (type == KEY_UNSPEC || type == KEY_RSA)) {
-		if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
-			r = SSH_ERR_ALLOC_FAIL;
-			goto out;
-		}
+		if ((prv = sshkey_new(KEY_UNSPEC)) == NULL)
+			return SSH_ERR_ALLOC_FAIL;
+
 		prv->rsa = EVP_PKEY_get1_RSA(pk);
 		prv->type = KEY_RSA;
 #ifdef DEBUG_PK
@@ -4596,7 +4567,8 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int
type,
 		    sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
 		    sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
 		    EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
-		    sshkey_ec_validate_private(prv->ecdsa) != 0) {
+		    (!external &&
+		     sshkey_ec_validate_private(prv->ecdsa) != 0)) {
 			r = SSH_ERR_INVALID_FORMAT;
 			goto out;
 		}
@@ -4609,15 +4581,56 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob,
int type,
 		r = SSH_ERR_INVALID_FORMAT;
 		goto out;
 	}
+	if (external)
+		prv->flags |= SSHKEY_FLAG_EXT;
 	r = 0;
 	if (keyp != NULL) {
 		*keyp = prv;
 		prv = NULL;
 	}
+ out:
+	sshkey_free(prv);
+	return r;
+}
+
+static int
+sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
+    const char *passphrase, struct sshkey **keyp)
+{
+	EVP_PKEY *pk = NULL;
+	BIO *bio = NULL;
+	int r;
+
+	if (keyp != NULL)
+		*keyp = NULL;
+
+	if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
+		return SSH_ERR_ALLOC_FAIL;
+	if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !+
(int)sshbuf_len(blob)) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+
+	clear_libcrypto_errors();
+	if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb,
+	    (char *)passphrase)) == NULL) {
+	       /*
+		* libcrypto may return various ASN.1 errors when attempting
+		* to parse a key with an incorrect passphrase.
+		* Treat all format errors as "incorrect passphrase" if a
+		* passphrase was supplied.
+		*/
+		if (passphrase != NULL && *passphrase != '\0')
+			r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+		else
+			r = convert_libcrypto_error();
+		goto out;
+	}
+	r = sshkey_openssl_private_key(type, pk, keyp, 0);
  out:
 	BIO_free(bio);
 	EVP_PKEY_free(pk);
-	sshkey_free(prv);
 	return r;
 }
 #endif /* WITH_OPENSSL */
diff --git a/sshkey.h b/sshkey.h
index 9c1d4f637..326996913 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -317,6 +317,11 @@ int ssh_xmss_verify(const struct sshkey *key,
     const u_char *data, size_t datalen, u_int compat);
 #endif

+#ifdef WITH_OPENSSL
+int sshkey_openssl_private_key(int type, EVP_PKEY *pk, struct sshkey **keyp,
+			       int external);
+#endif
+
 #if !defined(WITH_OPENSSL)
 # undef RSA
 # undef DSA
--
2.26.2