Hi all!

Using either GnuTLS or one of the TPM2 engines for OpenSSL, it's
possible to use keyfiles that are encrypted with a wrapping key from a
TPM2 device. Implementations have started to use special PEM headers for
these files. If openconnect it can automatically invoke the necessary
magic to unwrap the key without any user interaction. A similar patch
for wpa_supplicant can be found at
http://lists.infradead.org/pipermail/hostap/2019-July/040318.html.

Alas, these PEM files currently fail NM's header validation. The
attached patch just accepts these keys in NM, assuming further support
is present in the backend tools.

Kind regards,

Daniel
-- 
Daniel Kobras
Principal Architect
Puzzle ITC Deutschland
+49 7071 14316 0
www.puzzle-itc.de

-- 
Puzzle ITC Deutschland GmbH
Sitz der Gesellschaft: Jurastr. 27/1, 72072 
Tübingen

Eingetragen am Amtsgericht Stuttgart HRB 765802
Geschäftsführer: 
Lukas Kallies, Daniel Kobras, Mark Pröhl

From d47fa8a4d4fb97ef64bd4716ce7202139f9de098 Mon Sep 17 00:00:00 2001
From: Daniel Kobras <[email protected]>
Date: Mon, 24 Jun 2019 12:09:12 +0200
Subject: [PATCH] libnm/crypto: accept TPM2-wrapped PEM keys

Some tools that NM can interact with (eg. openconnect) have added
automated support to handle TPM2-wrapped PEM keys as drop-in
replacements for ordinary key files. Make sure that NM doesn't reject
these keys upfront. We cannot reliably assume NM to be able to unwrap
and validate the key. Therefore, accept any key as long as the PEM
header and trailer look ok.

Signed-off-by: Daniel Kobras <[email protected]>
---
 libnm-core/nm-crypto.c                        | 45 +++++++++++++++++++
 .../tests/certs/test-tpm2wrapped-key.pem      | 14 ++++++
 libnm-core/tests/test-keyfile.c               | 20 +++++++--
 3 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 libnm-core/tests/certs/test-tpm2wrapped-key.pem

diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c
index a708dce80..bf44763fa 100644
--- a/libnm-core/nm-crypto.c
+++ b/libnm-core/nm-crypto.c
@@ -49,6 +49,12 @@
 #define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
 #define PEM_PKCS8_DEC_KEY_END   "-----END PRIVATE KEY-----"
 
+#define PEM_TPM2_WRAPPED_KEY_BEGIN "-----BEGIN TSS2 PRIVATE KEY-----"
+#define PEM_TPM2_WRAPPED_KEY_END "-----END TSS2 PRIVATE KEY-----"
+
+#define PEM_TPM2_OLD_WRAPPED_KEY_BEGIN "-----BEGIN TSS2 KEY BLOB-----"
+#define PEM_TPM2_OLD_WRAPPED_KEY_END "-----END TSS2 KEY BLOB-----"
+
 /*****************************************************************************/
 
 static const NMCryptoCipherInfo cipher_infos[] = {
@@ -384,6 +390,43 @@ parse_pkcs8_key_file (const guint8 *data,
 	return TRUE;
 }
 
+static gboolean
+parse_tpm2_wrapped_key_file (const guint8 *data,
+                             gsize data_len,
+                             gboolean *out_encrypted,
+                             GError **error)
+{
+	gsize start = 0, end = 0;
+	const char *start_tag = NULL, *end_tag = NULL;
+
+	nm_assert (out_encrypted);
+
+	if (find_tag (PEM_TPM2_WRAPPED_KEY_BEGIN, data, data_len, 0, &start)) {
+		start_tag = PEM_TPM2_WRAPPED_KEY_BEGIN;
+		end_tag = PEM_TPM2_WRAPPED_KEY_END;
+	} else if (find_tag (PEM_TPM2_OLD_WRAPPED_KEY_BEGIN, data, data_len, 0, &start)) {
+		start_tag = PEM_TPM2_OLD_WRAPPED_KEY_BEGIN;
+		end_tag = PEM_TPM2_OLD_WRAPPED_KEY_END;
+	} else {
+		g_set_error_literal (error, NM_CRYPTO_ERROR,
+		                     NM_CRYPTO_ERROR_INVALID_DATA,
+		                     _("Failed to find expected TSS start tag."));
+		return FALSE;
+	}
+
+	start += strlen (start_tag);
+	if (!find_tag (end_tag, data, data_len, start, &end)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Failed to find expected TSS end tag '%s'."),
+		             end_tag);
+		return FALSE;
+	}
+
+	*out_encrypted = FALSE;
+	return TRUE;
+}
+
 static gboolean
 file_read_contents (const char *filename,
                     NMSecretPtr *out_contents,
@@ -822,6 +865,8 @@ nm_crypto_verify_private_key_data (const guint8 *data,
 			if (   !password
 			    || _nm_crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error))
 				format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+		} else if (parse_tpm2_wrapped_key_file (data, data_len, &is_encrypted, NULL)) {
+			format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
 		} else {
 			NMCryptoCipherType cipher;
 			nm_auto_free_secret char *iv = NULL;
diff --git a/libnm-core/tests/certs/test-tpm2wrapped-key.pem b/libnm-core/tests/certs/test-tpm2wrapped-key.pem
new file mode 100644
index 000000000..f3fd271c3
--- /dev/null
+++ b/libnm-core/tests/certs/test-tpm2wrapped-key.pem
@@ -0,0 +1,14 @@
+-----BEGIN TSS2 PRIVATE KEY-----
+MIICEwYGZ4EFCgEDoAMBAQECBQCBAAABBIIBGAEWAAEACwACBEAAAAAQABAIAAAA
+AAABAOJdEXw1LO6JWskHBSYQc1NfJAe9DOAyLA4XbXI5asQ8aNbmL51DP9mQQpqq
+a1CSRZAIuuorMxyRBBAFpF4OZqjNd/Nskp3iMmifr5yqAYZ3M31MqlBFiiyctqKp
+VwIChwsIbKelrsXbty1icP2CH+k4w/nPymPjnfYtgpMe8QW8n6U156ujIdJcISds
+QFcl3nrDnD1IumX0/LfanQrRDVSI+m6szvTrdsPMtGeaNMnlz0gx74auo7/CgEjX
+69xjPvQpNLHO/nV4EHSdfXH3LamcpWO8aEAVne3MFFA9V0bpv7uKoGhRjssD9kSr
+PQzNXfjHpkcOLeH4pzxDMFXDIGUEgeAA3gAgrCL3RRcBryFXToo9ZN3/f4EeeEjK
+58ejYomsxqvckhgAEMxbT26fo2h27b4KPlnUpoiL2JPLB0Xz6PJAF8n0YdJUO381
+xhzPTIQop81BxljTLV2C9WGns5bWDPW9ItEbv4UalEwFfxsW4Ma5smLWn3A1UwVN
+Z1cW/oUx+3nOCF1TZgbMPszToqCPlpIHd9vO709qpSyULIUkZLHS6PUM7ESY5U81
+f4BITxJR+aYaqErni/FDLsDVP0MQ3CuFHeUDHI0uEzzbfurYFjpp9+caaoWuZzpg
+VYV5pjPdgg==
+-----END TSS2 PRIVATE KEY-----
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 2c09b17c9..195f3797d 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -35,6 +35,7 @@
 #define TEST_CERT_DIR              NM_BUILD_SRCDIR"/libnm-core/tests/certs"
 #define TEST_WIRED_TLS_CA_CERT     TEST_CERT_DIR"/test-ca-cert.pem"
 #define TEST_WIRED_TLS_PRIVKEY     TEST_CERT_DIR"/test-key-and-cert.pem"
+#define TEST_WIRED_TLS_TPM2KEY     TEST_CERT_DIR"/test-tpm2wrapped-key.pem"
 
 /*****************************************************************************/
 
@@ -377,15 +378,15 @@ _test_8021x_cert_check_blob_full (NMConnection *con, const void *data, gsize len
 #define _test_8021x_cert_check_blob(con, data) _test_8021x_cert_check_blob_full(con, data, NM_STRLEN (data))
 
 static void
-test_8021x_cert (void)
+_test_8021x_cert_from_files (const char *cert, const char *key)
 {
 	NMSetting8021x *s_8021x;
 	gs_unref_object NMConnection *con = nmtst_create_minimal_connection ("test-cert", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
 	GError *error = NULL;
 	gboolean success;
 	NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
-	gs_free char *full_TEST_WIRED_TLS_CA_CERT = nmtst_file_resolve_relative_path (TEST_WIRED_TLS_CA_CERT, NULL);
-	gs_free char *full_TEST_WIRED_TLS_PRIVKEY = nmtst_file_resolve_relative_path (TEST_WIRED_TLS_PRIVKEY, NULL);
+	gs_free char *full_TEST_WIRED_TLS_CA_CERT = nmtst_file_resolve_relative_path (cert, NULL);
+	gs_free char *full_TEST_WIRED_TLS_PRIVKEY = nmtst_file_resolve_relative_path (key, NULL);
 
 	/* test writing/reading of certificates of NMSetting8021x */
 
@@ -444,6 +445,18 @@ test_8021x_cert (void)
 
 }
 
+static void
+test_8021x_cert (void)
+{
+	_test_8021x_cert_from_files (TEST_WIRED_TLS_CA_CERT, TEST_WIRED_TLS_PRIVKEY);
+}
+
+static void
+test_8021x_cert_tpm2key (void)
+{
+	_test_8021x_cert_from_files (TEST_WIRED_TLS_CA_CERT, TEST_WIRED_TLS_TPM2KEY);
+}
+
 /*****************************************************************************/
 
 static void
@@ -850,6 +863,7 @@ int main (int argc, char **argv)
 
 	g_test_add_func ("/core/keyfile/encode_key", test_encode_key);
 	g_test_add_func ("/core/keyfile/test_8021x_cert", test_8021x_cert);
+	g_test_add_func ("/core/keyfile/test_8021x_cert_tpm2key", test_8021x_cert_tpm2key);
 	g_test_add_func ("/core/keyfile/test_8021x_cert_read", test_8021x_cert_read);
 	g_test_add_func ("/core/keyfile/test_team_conf_read/valid", test_team_conf_read_valid);
 	g_test_add_func ("/core/keyfile/test_team_conf_read/invalid", test_team_conf_read_invalid);
-- 
2.20.1

_______________________________________________
networkmanager-list mailing list
[email protected]
https://mail.gnome.org/mailman/listinfo/networkmanager-list

Reply via email to