Rework PEM parsing to not be order dependent
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Mon, 20 Jun 2011 22:49:39 +0000 (18:49 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Wed, 27 Jul 2011 20:23:33 +0000 (16:23 -0400)
Some valid PEM file would not work because the private key was put
before the certificate.

gio/gtlscertificate.c

index 134f7a1..6e8eaf7 100644 (file)
@@ -209,70 +209,73 @@ g_tls_certificate_new_internal (const gchar  *certificate_pem,
 #define PEM_PRIVKEY_HEADER     "-----BEGIN RSA PRIVATE KEY-----"
 #define PEM_PRIVKEY_FOOTER     "-----END RSA PRIVATE KEY-----"
 
-static GTlsCertificate *
-parse_next_pem_certificate (const gchar **data,
-                           const gchar  *data_end,
-                           gboolean      required,
-                           GError      **error)
+gchar *
+parse_private_key (const gchar *data,
+                  gsize data_len,
+                  gboolean required,
+                  GError **error)
 {
-  const gchar *start, *end, *next;
-  gchar *cert_pem, *privkey_pem = NULL;
-  GTlsCertificate *cert;
+  const gchar *start, *end;
 
-  start = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER);
+  start = g_strstr_len (data, data_len, PEM_PRIVKEY_HEADER);
   if (!start)
     {
       if (required)
        {
          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                              _("No PEM-encoded certificate found"));
+                              _("No PEM-encoded private key found"));
        }
       return NULL;
     }
 
-  end = g_strstr_len (start, data_end - start, PEM_CERTIFICATE_FOOTER);
+  end = g_strstr_len (start, data_len - (data - start), PEM_PRIVKEY_FOOTER);
   if (!end)
     {
       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                          _("Could not parse PEM-encoded certificate"));
+                          _("Could not parse PEM-encoded private key"));
       return NULL;
     }
-  end += strlen (PEM_CERTIFICATE_FOOTER);
+  end += strlen (PEM_PRIVKEY_FOOTER);
   while (*end == '\r' || *end == '\n')
     end++;
 
-  cert_pem = g_strndup (start, end - start);
+  return g_strndup (start, end - start);
+}
 
-  *data = end;
 
-  next = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER);
-  start = g_strstr_len (*data, data_end - *data, PEM_PRIVKEY_HEADER);
-  if (start)
-    end = g_strstr_len (start, data_end - start, PEM_PRIVKEY_FOOTER);
+gchar *
+parse_next_pem_certificate (const gchar **data,
+                           const gchar  *data_end,
+                           gboolean      required,
+                           GError      **error)
+{
+  const gchar *start, *end;
 
-  if (start && (!next || start < next))
+  start = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER);
+  if (!start)
     {
-      if (!end || (next && end > next))
+      if (required)
        {
          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                              _("Could not parse PEM-encoded private key"));
-         return NULL;
+                              _("No PEM-encoded certificate found"));
        }
+      return NULL;
+    }
 
-      end += strlen (PEM_PRIVKEY_FOOTER);
-      while (*end == '\r' || *end == '\n')
-       end++;
-
-      privkey_pem = g_strndup (start, end - start);
-
-      *data = end + strlen (PEM_PRIVKEY_FOOTER);
+  end = g_strstr_len (start, data_end - start, PEM_CERTIFICATE_FOOTER);
+  if (!end)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                          _("Could not parse PEM-encoded certificate"));
+      return NULL;
     }
+  end += strlen (PEM_CERTIFICATE_FOOTER);
+  while (*end == '\r' || *end == '\n')
+    end++;
 
-  cert = g_tls_certificate_new_internal (cert_pem, privkey_pem, error);
-  g_free (cert_pem);
-  g_free (privkey_pem);
+  *data = end;
 
-  return cert;
+  return g_strndup (start, end - start);
 }
 
 /**
@@ -298,14 +301,32 @@ g_tls_certificate_new_from_pem  (const gchar  *data,
                                 GError      **error)
 {
   const gchar *data_end;
+  gchar *key_pem, *cert_pem;
+  GTlsCertificate *cert;
 
   g_return_val_if_fail (data != NULL, NULL);
 
   if (length == -1)
-    data_end = data + strlen (data);
-  else
-    data_end = data + length;
-  return parse_next_pem_certificate (&data, data_end, TRUE, error);
+    length = strlen (data);
+
+  data_end = data + length;
+
+  key_pem = parse_private_key (data, length, FALSE, error);
+  if (error && *error)
+    return NULL;
+
+  cert_pem = parse_next_pem_certificate (&data, data_end, TRUE, error);
+  if (error && *error)
+    {
+      g_free (key_pem);
+      return NULL;
+    }
+
+  cert = g_tls_certificate_new_internal (cert_pem, key_pem, error);
+  g_free (key_pem);
+  g_free (cert_pem);
+
+  return cert;
 }
 
 /**
@@ -359,18 +380,34 @@ g_tls_certificate_new_from_files (const gchar  *cert_file,
 {
   GTlsCertificate *cert;
   gchar *cert_data, *key_data;
+  gsize cert_len, key_len;
+  gchar *cert_pem, *key_pem;
+  const gchar *p;
 
-  if (!g_file_get_contents (cert_file, &cert_data, NULL, error))
+  if (!g_file_get_contents (cert_file, &cert_data, &cert_len, error))
+    return NULL;
+  p = cert_data;
+  cert_pem = parse_next_pem_certificate (&p, p + cert_len, TRUE, error);
+  g_free (cert_data);
+  if (error && *error)
     return NULL;
-  if (!g_file_get_contents (key_file, &key_data, NULL, error))
+
+  if (!g_file_get_contents (key_file, &key_data, &key_len, error))
     {
-      g_free (cert_data);
+      g_free (cert_pem);
       return NULL;
     }
-
-  cert = g_tls_certificate_new_internal (cert_data, key_data, error);
-  g_free (cert_data);
+  key_pem = parse_private_key (key_data, key_len, TRUE, error);
   g_free (key_data);
+  if (error && *error)
+    {
+      g_free (cert_pem);
+      return NULL;
+    }
+
+  cert = g_tls_certificate_new_internal (cert_pem, key_pem, error);
+  g_free (cert_pem);
+  g_free (key_pem);
   return cert;
 }
 
@@ -395,8 +432,7 @@ GList *
 g_tls_certificate_list_new_from_file (const gchar  *file,
                                      GError      **error)
 {
-  GTlsCertificate *cert;
-  GList *list, *l;
+  GQueue queue = G_QUEUE_INIT;
   gchar *contents, *end;
   const gchar *p;
   gsize length;
@@ -404,25 +440,30 @@ g_tls_certificate_list_new_from_file (const gchar  *file,
   if (!g_file_get_contents (file, &contents, &length, error))
     return NULL;
 
-  list = NULL;
   end = contents + length;
   p = contents;
   while (p && *p)
     {
-      cert = parse_next_pem_certificate (&p, end, FALSE, error);
+      gchar *cert_pem;
+      GTlsCertificate *cert = NULL;
+
+      cert_pem = parse_next_pem_certificate (&p, end, FALSE, error);
+      if (cert_pem)
+       {
+         cert = g_tls_certificate_new_internal (cert_pem, NULL, error);
+         g_free (cert_pem);
+       }
       if (!cert)
        {
-         for (l = list; l; l = l->next)
-           g_object_unref (l->data);
-         g_list_free (list);
-         list = NULL;
+         g_list_free_full (queue.head, g_object_unref);
+         queue.head = NULL;
          break;
        }
-      list = g_list_prepend (list, cert);
+      g_queue_push_tail (&queue, cert);
     }
 
   g_free (contents);
-  return g_list_reverse (list);
+  return queue.head;
 }