[glib: 3/4] gtlscertificate: Allow any type of private key in PEM files



commit a437a506944a4ddd6ec31df5c302e0fe368ba438
Author: Fredrik Ternerot <fredrikt axis com>
Date:   Mon Dec 10 08:48:45 2018 +0100

    gtlscertificate: Allow any type of private key in PEM files
    
    Allow any type of private key in PEM files by treating PEM guards ending
    with "PRIVATE KEY-----" as a private key instead of looking for a
    pre-defined set of PEM guards. This enables the possibility for custom
    GTlsBackend to add support for new key types.
    
    Test cases have been expanded to ensure PEM parsing works for private
    key when either header or footer is missing.
    
    Encrypted PKCS#8 is still rejected. Test case has been added for this to
    ensure behaviour is the same before and after this change.

 gio/gtlscertificate.c                       | 69 +++++++++++++++--------------
 gio/tests/Makefile.am                       | 25 ++++++-----
 gio/tests/cert-tests/key8enc.pem            | 18 ++++++++
 gio/tests/cert-tests/key_missing-footer.pem | 14 ++++++
 gio/tests/cert-tests/key_missing-header.pem | 14 ++++++
 gio/tests/tls-certificate.c                 | 33 ++++++++++++++
 6 files changed, 128 insertions(+), 45 deletions(-)
---
diff --git a/gio/gtlscertificate.c b/gio/gtlscertificate.c
index 1ec48f118..72de5eb1f 100644
--- a/gio/gtlscertificate.c
+++ b/gio/gtlscertificate.c
@@ -218,12 +218,11 @@ g_tls_certificate_new_internal (const gchar      *certificate_pem,
 
 #define PEM_CERTIFICATE_HEADER     "-----BEGIN CERTIFICATE-----"
 #define PEM_CERTIFICATE_FOOTER     "-----END CERTIFICATE-----"
-#define PEM_PKCS1_PRIVKEY_HEADER   "-----BEGIN RSA PRIVATE KEY-----"
-#define PEM_PKCS1_PRIVKEY_FOOTER   "-----END RSA PRIVATE KEY-----"
-#define PEM_PKCS8_PRIVKEY_HEADER   "-----BEGIN PRIVATE KEY-----"
-#define PEM_PKCS8_PRIVKEY_FOOTER   "-----END PRIVATE KEY-----"
+#define PEM_PRIVKEY_HEADER_BEGIN   "-----BEGIN "
+#define PEM_PRIVKEY_HEADER_END     "PRIVATE KEY-----"
+#define PEM_PRIVKEY_FOOTER_BEGIN   "-----END "
+#define PEM_PRIVKEY_FOOTER_END     "PRIVATE KEY-----"
 #define PEM_PKCS8_ENCRYPTED_HEADER "-----BEGIN ENCRYPTED PRIVATE KEY-----"
-#define PEM_PKCS8_ENCRYPTED_FOOTER "-----END ENCRYPTED PRIVATE KEY-----"
 
 static gchar *
 parse_private_key (const gchar *data,
@@ -231,45 +230,47 @@ parse_private_key (const gchar *data,
                   gboolean required,
                   GError **error)
 {
-  const gchar *start, *end, *footer;
+  const gchar *header_start = NULL, *header_end, *footer_start = NULL, *footer_end;
 
-  start = g_strstr_len (data, data_len, PEM_PKCS1_PRIVKEY_HEADER);
-  if (start)
-    footer = PEM_PKCS1_PRIVKEY_FOOTER;
-  else
+  header_end = g_strstr_len (data, data_len, PEM_PRIVKEY_HEADER_END);
+  if (header_end)
+    header_start = g_strrstr_len (data, header_end - data, PEM_PRIVKEY_HEADER_BEGIN);
+
+  if (!header_start)
     {
-      start = g_strstr_len (data, data_len, PEM_PKCS8_PRIVKEY_HEADER);
-      if (start)
-       footer = PEM_PKCS8_PRIVKEY_FOOTER;
-      else
-       {
-         start = g_strstr_len (data, data_len, PEM_PKCS8_ENCRYPTED_HEADER);
-         if (start)
-           {
-             g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                                  _("Cannot decrypt PEM-encoded private key"));
-           }
-         else if (required)
-           {
-             g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                                  _("No PEM-encoded private key found"));
-           }
-         return NULL;
-       }
+      if (required)
+       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                            _("No PEM-encoded private key found"));
+
+      return NULL;
     }
 
-  end = g_strstr_len (start, data_len - (start - data), footer);
-  if (!end)
+  header_end += strlen (PEM_PRIVKEY_HEADER_END);
+
+  if (strncmp (header_start, PEM_PKCS8_ENCRYPTED_HEADER, header_end - header_start) == 0)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                          _("Cannot decrypt PEM-encoded private key"));
+      return NULL;
+    }
+
+  footer_end = g_strstr_len (header_end, data_len - (header_end - data), PEM_PRIVKEY_FOOTER_END);
+  if (footer_end)
+    footer_start = g_strrstr_len (header_end, footer_end - header_end, PEM_PRIVKEY_FOOTER_BEGIN);
+
+  if (!footer_start)
     {
       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
                           _("Could not parse PEM-encoded private key"));
       return NULL;
     }
-  end += strlen (footer);
-  while (*end == '\r' || *end == '\n')
-    end++;
 
-  return g_strndup (start, end - start);
+  footer_end += strlen (PEM_PRIVKEY_FOOTER_END);
+
+  while (*footer_end == '\r' || *footer_end == '\n')
+    footer_end++;
+
+  return g_strndup (header_start, footer_end - header_start);
 }
 
 
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 0ad61e4d1..d07792e66 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -225,17 +225,20 @@ tls_certificate_SOURCES = \
 dist_test_data += $(cert_data_files)
 cert_data_files = $(addprefix cert-tests/,$(cert_tests))
 cert_tests = \
-       cert1.pem       \
-       cert2.pem       \
-       cert3.pem       \
-       cert-crlf.pem   \
-       cert-key.pem    \
-       cert-list.pem   \
-       key8.pem        \
-       key-cert.pem    \
-       key.pem         \
-       key-crlf.pem    \
-       nothing.pem     \
+       cert1.pem               \
+       cert2.pem               \
+       cert3.pem               \
+       cert-crlf.pem           \
+       cert-key.pem            \
+       cert-list.pem           \
+       key8.pem                \
+       key8enc.pem             \
+       key-cert.pem            \
+       key.pem                 \
+       key-crlf.pem            \
+       key_missing-footer.pem  \
+       key_missing-header.pem  \
+       nothing.pem             \
        $(NULL)
 
 uninstalled_test_extra_programs += socket-client
diff --git a/gio/tests/cert-tests/key8enc.pem b/gio/tests/cert-tests/key8enc.pem
new file mode 100644
index 000000000..26a7157b4
--- /dev/null
+++ b/gio/tests/cert-tests/key8enc.pem
@@ -0,0 +1,18 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIdjDoEOJTH2ICAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDNLC2sDVjClaQyT8BfXTt1BIIC
+gCN4s9Z5bmfKogL7YHIJly2zLX5uILHeCr3iQpoPS8057V9Af1wqB/8AUOJrLY96
+R2amkXjlxuqA0BebEk4gcR4tWvCNQ2VCOqvQozUt8LnA+2xQRgzNwaW0HPxcAUzf
+6GVZKL7xfpwFD2ootfLwTHB2zAIVMo8nwgEzdDz93ZwsMmXJmOfSO7vpDQUnVqUX
+jVlue0i8n7fO4ClQ8fz5J8zyvPj403bR9qxsIJjQZACNVLMIksQXjTDngymy/ziI
+lZD4JDLXCQwAOgFz6N6vsyD/mHROyL4/4q8ujYFPmVpuAlQzuZJe6TFnmZHiSfoI
+we6wi1Nee1rbM4VzsGFzMa4Fr0ZhElHEKBXXje4YKWCAOWEo3tLjow4+0dQxNx5W
+tsbQdRt2fRYNYTgt18O55kq3DVfy93aMQVYIMuXkxwAuCWBeiLQrCfAM5r7kDwfc
+owp2AQ5Ndf+aAwr89k2fYUpexz9kZzU+eIY2K1cRhpUlLRAr5SG2oVy7n9IvYs1m
+O7/hjVBvXeAPDADVOtx/YNxPYr9ZI1X2QNDYGxNuSUNF1qGps66Gj+fcRe2NO+Ej
+YfSyfBvw+0h8sad81ZPepCSpIkYX91p6lCdCmRnJWYBwYyn6V5tXOx6tn5ntKJZ9
+9OtTGr7CMm7PLs9S8b03MV9IDJH+TBqR7msP1KWZbTxCNOws28EXo75tQ51ywElF
+FJI6ZU2gBYaX39i8WyvEMXFRRqzYUMzV0Yw2KeVRiGLh0ZX/4rlh2PQqVXGyakvn
+XttDRKEYPEvXDSRpO+tIvESlq9T0Pfo/rpnD4xJd2JWO6z/CSrn8cujs80e1+YjT
+HXksoJzsoLGeiYG2DzTK9lY=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/gio/tests/cert-tests/key_missing-footer.pem b/gio/tests/cert-tests/key_missing-footer.pem
new file mode 100644
index 000000000..3a5b38d25
--- /dev/null
+++ b/gio/tests/cert-tests/key_missing-footer.pem
@@ -0,0 +1,14 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCslrTl+gp3lQsEGMegjhQMow5yhinpQ7yji36soGuUpUynQPs7
+P5tm3ojZPeQGNmYof3Csc2yTrMLrCidt8xFkcgtZydOJ9sQSEgwc2of4nlvffIZb
+dIR+13WOPVRmS5tBVkQWFrlSFtQ70BDhNK3SZfkAb4+QmLSR1MJFaUPWrQIDAQAB
+AoGAUTnskYAIhRdEQ/1Vlp7HmNr05bl26C3VDjOMvroRZ7gUR3MxykS5YsTBK10R
+gEsB8XVpFgCMzUO1yODShdCsEg9kCB3fzSWkunK8+TF2TKOM5uWlQwifKJvcNisR
+Nbg3r8WygMMXaWSFA3xWoRuZ5It0jOX18v+x5RHHon/kaRECQQDl6FSwgJLeNAkR
+pMNQGdRhmMesHWmNNBv3Wozqm6Wpkwo5ZXPsLt3pprd0GN5jX0IG7clT1/eMD9/G
++3UGqTj3AkEAwC0M2gv+QUhbaB+KSlOZDOi4gsnhnsnaM7HQGDJJ5no4y2EvnYI3
+Y5rPJWedeYlCV3ccMitjnjcIJHInRZBIewJBANgsamVDn9Ua7GQQni1U/COAek7V
+oQfKNXmRROrbyxr1TSnGwQcU0kf+IIUjVQfu67CEKUeSzAqAapM4oULQHuUCQQC9
+J9qdiO6DXXAzRdA9pplgHnT2rzV3sSEoft3f4yfgRu8+KHPQqkpQrSE1pQ5YgWUe
+aGwFabXNFkfab839562fAkBl8jPidQdKWEgSa6h5pm4++sXLdWl7p6jiyetH64W7
+HnhRryE3ptrRGO0hSV1v4bx3DKzeJiJRlWUWiSl7828t
diff --git a/gio/tests/cert-tests/key_missing-header.pem b/gio/tests/cert-tests/key_missing-header.pem
new file mode 100644
index 000000000..4bcb3674f
--- /dev/null
+++ b/gio/tests/cert-tests/key_missing-header.pem
@@ -0,0 +1,14 @@
+MIICXQIBAAKBgQCslrTl+gp3lQsEGMegjhQMow5yhinpQ7yji36soGuUpUynQPs7
+P5tm3ojZPeQGNmYof3Csc2yTrMLrCidt8xFkcgtZydOJ9sQSEgwc2of4nlvffIZb
+dIR+13WOPVRmS5tBVkQWFrlSFtQ70BDhNK3SZfkAb4+QmLSR1MJFaUPWrQIDAQAB
+AoGAUTnskYAIhRdEQ/1Vlp7HmNr05bl26C3VDjOMvroRZ7gUR3MxykS5YsTBK10R
+gEsB8XVpFgCMzUO1yODShdCsEg9kCB3fzSWkunK8+TF2TKOM5uWlQwifKJvcNisR
+Nbg3r8WygMMXaWSFA3xWoRuZ5It0jOX18v+x5RHHon/kaRECQQDl6FSwgJLeNAkR
+pMNQGdRhmMesHWmNNBv3Wozqm6Wpkwo5ZXPsLt3pprd0GN5jX0IG7clT1/eMD9/G
++3UGqTj3AkEAwC0M2gv+QUhbaB+KSlOZDOi4gsnhnsnaM7HQGDJJ5no4y2EvnYI3
+Y5rPJWedeYlCV3ccMitjnjcIJHInRZBIewJBANgsamVDn9Ua7GQQni1U/COAek7V
+oQfKNXmRROrbyxr1TSnGwQcU0kf+IIUjVQfu67CEKUeSzAqAapM4oULQHuUCQQC9
+J9qdiO6DXXAzRdA9pplgHnT2rzV3sSEoft3f4yfgRu8+KHPQqkpQrSE1pQ5YgWUe
+aGwFabXNFkfab839562fAkBl8jPidQdKWEgSa6h5pm4++sXLdWl7p6jiyetH64W7
+HnhRryE3ptrRGO0hSV1v4bx3DKzeJiJRlWUWiSl7828t
+-----END RSA PRIVATE KEY-----
diff --git a/gio/tests/tls-certificate.c b/gio/tests/tls-certificate.c
index 02bcf5eec..e1ba23737 100644
--- a/gio/tests/tls-certificate.c
+++ b/gio/tests/tls-certificate.c
@@ -260,6 +260,22 @@ from_files (const Reference *ref)
   g_clear_error (&error);
   g_assert_null (cert);
 
+  /* Missing header private key */
+  cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", 
NULL),
+                                           g_test_get_filename (G_TEST_DIST, "cert-tests", 
"key_missing-header.pem", NULL),
+                                           &error);
+  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+  g_clear_error (&error);
+  g_assert_null (cert);
+
+  /* Missing footer private key */
+  cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", 
NULL),
+                                           g_test_get_filename (G_TEST_DIST, "cert-tests", 
"key_missing-footer.pem", NULL),
+                                           &error);
+  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+  g_clear_error (&error);
+  g_assert_null (cert);
+
   /* Missing certificate */
   cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
                                            g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
@@ -333,6 +349,21 @@ from_files_pkcs8 (const Reference *ref)
   g_object_unref (cert);
 }
 
+static void
+from_files_pkcs8enc (const Reference *ref)
+{
+  GTlsCertificate *cert;
+  GError *error = NULL;
+
+  /* Mare sure an error is returned for encrypted key */
+  cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", 
NULL),
+                                           g_test_get_filename (G_TEST_DIST, "cert-tests", "key8enc.pem", 
NULL),
+                                           &error);
+  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+  g_clear_error (&error);
+  g_assert_null (cert);
+}
+
 static void
 list_from_file (const Reference *ref)
 {
@@ -429,6 +460,8 @@ main (int   argc,
                         &ref, (GTestDataFunc)from_files_crlf);
   g_test_add_data_func ("/tls-certificate/from_files_pkcs8",
                         &ref, (GTestDataFunc)from_files_pkcs8);
+  g_test_add_data_func ("/tls-certificate/from_files_pkcs8enc",
+                        &ref, (GTestDataFunc)from_files_pkcs8enc);
   g_test_add_data_func ("/tls-certificate/list_from_file",
                         &ref, (GTestDataFunc)list_from_file);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]