All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Zaborowski <andrew.zaborowski@intel.com>
To: ell@lists.01.org
Subject: [PATCH 08/13] pem: Add l_pem_load_container_file
Date: Thu, 10 Dec 2020 20:32:01 +0100	[thread overview]
Message-ID: <20201210193202.526451-8-andrew.zaborowski@intel.com> (raw)
In-Reply-To: <20201210193202.526451-1-andrew.zaborowski@intel.com>

[-- Attachment #1: Type: text/plain, Size: 7631 bytes --]

Add a function that takes a file path and detects what X.509 certificate
and/or private key format it uses and parses it accordingly.  This is to
make it easier for clients to support multiple file formats, including
raw X.509 certificates -- without this they would have to load the
contents of the file and call l_cert_new_from_der().
l_pem_load_container can also be used instead of
l_pem_load_certificate_chain() and l_pem_load_private_key().

This function can now load binary certificate files which are not PEM
but there wasn't a better place for it than pem.c.  I guess the name
could also be improved to imply that it's for certificate and private
key container files, but I couldn't come up with a name that isn't too
long.

This function treats PEM files as containers that can have both private
keys and certificates at the same time, openssl can parse such files and
also produces such files in some situations and they may have some good
uses.
---
 ell/ell.sym |   1 +
 ell/pem.c   | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 ell/pem.h   |   5 ++
 3 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/ell/ell.sym b/ell/ell.sym
index 49689d1..239ab65 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -415,6 +415,7 @@ global:
 	l_pem_load_file;
 	l_pem_load_private_key;
 	l_pem_load_private_key_from_data;
+	l_pem_load_container_file;
 	/* pkcs5 */
 	l_pkcs5_pbkdf1;
 	l_pkcs5_pbkdf2;
diff --git a/ell/pem.c b/ell/pem.c
index 1b995d5..90d06fb 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -195,7 +195,7 @@ const char *pem_next(const void *buf, size_t buf_len, char **type_label,
 
 static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 				char **out_type_label, size_t *out_len,
-				char **out_headers)
+				char **out_headers, const char **out_endp)
 {
 	size_t base64_len;
 	const char *base64;
@@ -203,7 +203,7 @@ static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 	uint8_t *ret;
 
 	base64 = pem_next(buf, buf_len, &label, &base64_len,
-				NULL, false);
+				out_endp, false);
 	if (!base64)
 		return NULL;
 
@@ -262,7 +262,7 @@ static uint8_t *pem_load_buffer(const void *buf, size_t buf_len,
 LIB_EXPORT uint8_t *l_pem_load_buffer(const void *buf, size_t buf_len,
 					char **type_label, size_t *out_len)
 {
-	return pem_load_buffer(buf, buf_len, type_label, out_len, NULL);
+	return pem_load_buffer(buf, buf_len, type_label, out_len, NULL, NULL);
 }
 
 struct pem_file_info {
@@ -315,7 +315,8 @@ static uint8_t *pem_load_file(const char *filename, char **out_type_label,
 		return NULL;
 
 	result = pem_load_buffer(file.data, file.st.st_size,
-					out_type_label, out_len, out_headers);
+					out_type_label, out_len, out_headers,
+					NULL);
 	pem_file_close(&file);
 	return result;
 }
@@ -888,7 +889,7 @@ LIB_EXPORT struct l_key *l_pem_load_private_key_from_data(const void *buf,
 	if (encrypted)
 		*encrypted = false;
 
-	content = pem_load_buffer(buf, buf_len, &label, &len, &headers);
+	content = pem_load_buffer(buf, buf_len, &label, &len, &headers, NULL);
 
 	if (!content)
 		return NULL;
@@ -933,3 +934,149 @@ LIB_EXPORT struct l_key *l_pem_load_private_key(const char *filename,
 	return pem_load_private_key(content, len, label, passphrase, headers,
 					encrypted);
 }
+
+/*
+ * Look at a file, try to detect which of the few X.509 certificate and/or
+ * private key container formats it uses and load any certificates in it as
+ * a certificate chain object, and load the first private key as an l_key
+ * object.
+ *
+ * Currently supported are:
+ *  PEM X.509 certificates
+ *  PEM PKCS#8 encrypted and unenecrypted private keys
+ *  PEM legacy PKCS#1 encrypted and unenecrypted private keys
+ *  Raw X.509 certificates (.cer, .der, .crt)
+ *
+ * The raw format contains exactly one certificate, PEM and PKCS#12 files
+ * can contain any combination of certificates and private keys.
+ */
+LIB_EXPORT void l_pem_load_container_file(const char *filename,
+					const char *password,
+					struct l_certchain **out_certchain,
+					struct l_key **out_privkey,
+					bool *out_encrypted)
+{
+	struct pem_file_info file;
+	const char *ptr;
+	size_t len;
+	bool error = false;
+
+	if (out_encrypted)
+		*out_encrypted = false;
+
+	if (out_certchain)
+		*out_certchain = NULL;
+
+	if (out_privkey)
+		*out_privkey = NULL;
+
+	if (unlikely(!filename))
+		return;
+
+	if (pem_file_open(&file, filename) < 0)
+		return;
+
+	if (file.st.st_size < 1)
+		goto close;
+
+	/* See if we have a DER sequence tag@the start */
+	if (file.data[0] == ASN1_ID_SEQUENCE) {
+		const uint8_t *seq_data;
+		const uint8_t *elem_data;
+		size_t elem_len;
+		uint8_t tag;
+
+		if (!(seq_data = asn1_der_find_elem(file.data, file.st.st_size,
+							0, &tag, &len)))
+			goto not_der_after_all;
+
+		/*
+		 * See if the first sub-element is another sequence, then, out
+		 * of the formats that we currently support this can only be a
+		 * raw certificate.  If integer, it's going to be PKCS#12.  If
+		 * we wish to add any more formats we'll probably need to start
+		 * guessing from the filename suffix.
+		 */
+		if (!(elem_data = asn1_der_find_elem(seq_data, len,
+							0, &tag, &elem_len)))
+			goto not_der_after_all;
+
+		if (tag == ASN1_ID_SEQUENCE) {
+			if (out_certchain) {
+				struct l_cert *cert;
+
+				if (!(cert = l_cert_new_from_der(file.data,
+							file.st.st_size)))
+					goto close;
+
+				*out_certchain = certchain_new_from_leaf(cert);
+			}
+
+			goto close;
+		}
+	}
+
+not_der_after_all:
+	/*
+	 * RFC 7486 allows whitespace and possibly other data before the
+	 * PEM "encapsulation boundary" so rather than check if the start
+	 * of the data looks like PEM, we fall back to this format if the
+	 * data didn't look like anything else we knew about.
+	 */
+	ptr = (const char *) file.data;
+	len = file.st.st_size;
+	while (!error && len) {
+		uint8_t *der;
+		size_t der_len;
+		char *type_label;
+		char *headers;
+		const char *endp;
+
+		if (!(der = pem_load_buffer(ptr, len, &type_label, &der_len,
+						&headers, &endp)))
+			break;
+
+		len -= endp - ptr;
+		ptr = endp;
+
+		if (!strcmp(type_label, "CERTIFICATE")) {
+			struct l_cert *cert;
+
+			if (!out_certchain)
+				goto next;
+
+			if (!(cert = l_cert_new_from_der(der, der_len))) {
+				l_certchain_free(*out_certchain);
+				*out_certchain = NULL;
+				error = true;
+				goto next;
+			}
+
+			if (!*out_certchain)
+				*out_certchain = certchain_new_from_leaf(cert);
+			else
+				certchain_link_issuer(*out_certchain, cert);
+
+			goto next;
+		}
+
+		/* Only use the first private key found */
+		if (out_privkey && !*out_privkey) {
+			*out_privkey = pem_load_private_key(der, der_len,
+								type_label,
+								password,
+								headers,
+								out_encrypted);
+			continue;
+		}
+
+next:
+		explicit_bzero(der, der_len);
+		l_free(der);
+		l_free(type_label);
+		l_free(headers);
+	}
+
+close:
+	pem_file_close(&file);
+}
diff --git a/ell/pem.h b/ell/pem.h
index 1c93e7a..ce5af04 100644
--- a/ell/pem.h
+++ b/ell/pem.h
@@ -50,6 +50,11 @@ struct l_key *l_pem_load_private_key_from_data(const void *buf, size_t len,
 						const char *passphrase,
 						bool *encrypted);
 
+void l_pem_load_container_file(const char *filename, const char *password,
+				struct l_certchain **out_certchain,
+				struct l_key **out_privkey,
+				bool *out_encrypted);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.27.0

  parent reply	other threads:[~2020-12-10 19:32 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-10 19:31 [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 02/13] pem: Factor out the PKCS#8 decoding code Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 03/13] cipher: Add an RC2 implementation Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 04/13] cipher: Add ARC4 implementation Andrew Zaborowski
2020-12-10 22:46   ` Denis Kenzior
2020-12-10 19:31 ` [PATCH 05/13] pem: Don't expect padding with stream ciphers Andrew Zaborowski
2020-12-10 19:31 ` [PATCH 06/13] pkcs5: Add the PKCS#12 KDF Andrew Zaborowski
2020-12-10 22:51   ` Denis Kenzior
2020-12-11 10:34     ` Andrew Zaborowski
2020-12-11 15:50       ` Denis Kenzior
2020-12-12  0:06         ` Andrew Zaborowski
2020-12-10 19:32 ` [PATCH 07/13] pkcs5: Add PKCS#12 algorithms in pkcs5_cipher_from_alg_id Andrew Zaborowski
2020-12-10 19:32 ` Andrew Zaborowski [this message]
2020-12-10 19:32 ` [PATCH 09/13] pem: Add PKCS#12 parsing Andrew Zaborowski
2020-12-10 22:47 ` [PATCH 01/13] pem: Wipe more memory in PKCS#1 private key decoding Denis Kenzior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201210193202.526451-8-andrew.zaborowski@intel.com \
    --to=andrew.zaborowski@intel.com \
    --cc=ell@lists.01.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.