]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Changed prototype for gnutls_pkcs12_simple_parse() to simplify chain building.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 9 Jun 2012 09:02:07 +0000 (11:02 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 9 Jun 2012 09:47:46 +0000 (11:47 +0200)
lib/gnutls_x509.c
lib/includes/gnutls/pkcs12.h
lib/x509/pkcs12.c
tests/Makefile.am
tests/pkcs12-decode/Makefile.am
tests/pkcs12-decode/pkcs12_5certs.p12 [new file with mode: 0644]
tests/pkcs12_simple.c [new file with mode: 0644]

index 96656d24722d7e2c983527c73f1e1f32d90ba8db..62d547cc827a22fdd7fbd1070791271a3c9a95b4 100644 (file)
@@ -1888,8 +1888,6 @@ int
   return ret;
 }
 
-#define MAX_CERT_LIST 16
-
 /**
  * gnutls_certificate_set_x509_simple_pkcs12_mem:
  * @res: is a #gnutls_certificate_credentials_t structure.
@@ -1928,10 +1926,9 @@ int
 {
   gnutls_pkcs12_t p12;
   gnutls_x509_privkey_t key = NULL;
-  gnutls_x509_crt_t cert = NULL;
-  gnutls_x509_crt_t *extra_certs = NULL;
+  gnutls_x509_crt_t *chain = NULL;
   gnutls_x509_crl_t crl = NULL;
-  unsigned int extra_certs_size = 0, i;
+  unsigned int chain_size = 0, i;
   int ret;
 
   ret = gnutls_pkcs12_init (&p12);
@@ -1960,8 +1957,8 @@ int
         }
     }
 
-  ret = gnutls_pkcs12_simple_parse (p12, password, &key, &cert
-                             &extra_certs, &extra_certs_size, &crl);
+  ret = gnutls_pkcs12_simple_parse (p12, password, &key, &chain, &chain_size
+                             NULL, NULL, &crl, 0);
   gnutls_pkcs12_deinit (p12);
   if (ret < 0)
     {
@@ -1969,41 +1966,8 @@ int
       return ret;
     }
 
-  if (key && cert)
+  if (key && chain)
     {
-      gnutls_x509_crt_t chain[MAX_CERT_LIST];
-      unsigned int chain_size = 1, j;
-      unsigned int done = 0;
-      
-      j = 0;
-      chain[j] = cert;
-
-      if (extra_certs_size > 0 && extra_certs_size < MAX_CERT_LIST-1)
-        {
-          do 
-            {
-              for (i=0;i<extra_certs_size;i++)
-                {
-                  if (gnutls_x509_crt_check_issuer(chain[j], extra_certs[i]) != 0 &&
-                      gnutls_x509_crt_check_issuer(extra_certs[i], chain[j]) == 0)
-                    {
-                      if (gnutls_x509_crt_check_issuer(extra_certs[i], extra_certs[i]) != 0)
-                        { /* we found a self-signed one. We are done. */
-                          done = 1;
-                          break;
-                        }
-                      chain[++j] = extra_certs[i];
-                      chain_size++;
-                      break;
-                    }
-
-                  if (i==extra_certs_size - 1) 
-                    done = 1;
-                }
-            }
-          while(done == 0);
-        }
-
       ret = gnutls_certificate_set_x509_key (res, chain, chain_size, key);
       if (ret < 0)
         {
@@ -2011,6 +1975,12 @@ int
           goto done;
         }
     }
+  else
+    {
+      gnutls_assert();
+      ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+      goto done;
+    }
 
   if (crl)
     {
@@ -2025,14 +1995,12 @@ int
   ret = 0;
 
 done:
-  if (extra_certs)
+  if (chain)
     {
-      for (i=0;i<extra_certs_size;i++)
-        gnutls_x509_crt_deinit (extra_certs[i]);
-      gnutls_free(extra_certs);
+      for (i=0;i<chain_size;i++)
+        gnutls_x509_crt_deinit (chain[i]);
+      gnutls_free(chain);
     }
-  if (cert)
-    gnutls_x509_crt_deinit (cert);
   if (key)
     gnutls_x509_privkey_deinit (key);
   if (crl)
index dde5b842752e902c7e2f5b9f893a2a3fae90b2c1..14b45f8ef3aa1e69439844a99b9471eb428b8396 100644 (file)
@@ -57,14 +57,15 @@ extern "C"
   int gnutls_pkcs12_bag_decrypt (gnutls_pkcs12_bag_t bag, const char *pass);
   int gnutls_pkcs12_bag_encrypt (gnutls_pkcs12_bag_t bag, const char *pass,
                                  unsigned int flags);
-  int gnutls_pkcs12_simple_parse (
-                           gnutls_pkcs12_t p12,
-                           const char *password,
-                           gnutls_x509_privkey_t * key,
-                           gnutls_x509_crt_t * cert,
-                           gnutls_x509_crt_t ** extra_certs_ret,
-                           unsigned int * extra_certs_ret_len,
-                           gnutls_x509_crl_t * crl);
+  int gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
+                     const char *password,
+                     gnutls_x509_privkey_t * key,
+                     gnutls_x509_crt_t ** chain,
+                     unsigned int * chain_len,
+                     gnutls_x509_crt_t ** extra_certs,
+                     unsigned int * extra_certs_len,
+                     gnutls_x509_crl_t * crl,
+                     unsigned int flags);
 
 /**
  * gnutls_pkcs12_bag_type_t:
index 78e4075097900ba199b6a438da64368b012cc71b..a595488b32ae36b877420fa8b06e298d51e3b7c1 100644 (file)
@@ -1328,20 +1328,61 @@ cleanup:
 
 }
 
+/* Checks if the extra_certs contain certificates that may form a chain
+ * with the first certificate in chain (it is expected that chain_len==1)
+ * and appends those in the chain.
+ */
+static int make_chain(gnutls_x509_crt_t **chain, unsigned int *chain_len,
+                      gnutls_x509_crt_t **extra_certs, unsigned int *extra_certs_len)
+{
+unsigned int i;
+
+  if (*chain_len != 1)
+    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+  
+  i = 0;
+  while(i<*extra_certs_len)
+    {
+      /* if it is an issuer but not a self-signed one */
+      if (gnutls_x509_crt_check_issuer((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0 &&
+          gnutls_x509_crt_check_issuer((*extra_certs)[i], (*extra_certs)[i]) == 0)
+        {
+           *chain = gnutls_realloc (*chain, sizeof((*chain)[0]) *
+                                                     ++(*chain_len));
+           if (*chain == NULL)
+             {
+               gnutls_assert();
+               return GNUTLS_E_MEMORY_ERROR;
+             }
+           (*chain)[*chain_len - 1] = (*extra_certs)[i];
+           
+           (*extra_certs)[i] = (*extra_certs)[*extra_certs_len-1];
+           (*extra_certs_len)--;
+
+           i=0;
+           continue;
+        }
+      i++;
+    }
+  return 0;
+}
+
 /**
  * gnutls_pkcs12_simple_parse:
  * @p12: the PKCS#12 blob.
  * @password: optional password used to decrypt PKCS#12 blob, bags and keys.
  * @key: a structure to store the parsed private key.
- * @cert: a structure to store the parsed certificate.
- * @extra_certs_ret: optional pointer to receive an array of additional
+ * @chain: the corresponding to key certificate chain
+ * @chain_len: will be updated with the number of additional
+ * @extra_certs: optional pointer to receive an array of additional
  *                   certificates found in the PKCS#12 blob.
- * @extra_certs_ret_len: will be updated with the number of additional
+ * @extra_certs_len: will be updated with the number of additional
  *                       certs.
- * @crl: a structure to store the parsed CRL.
+ * @crl: an optional structure to store the parsed CRL.
+ * @flags: should be zero
  *
  * This function parses a PKCS#12 blob in @p12blob and extracts the
- * private key, the corresponding certificate, and any additional
+ * private key, the corresponding certificate chain, and any additional
  * certificates and a CRL.
  *
  * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
@@ -1378,14 +1419,18 @@ int
 gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
                      const char *password,
                      gnutls_x509_privkey_t * key,
-                     gnutls_x509_crt_t * cert,
-                     gnutls_x509_crt_t ** extra_certs_ret,
-                     unsigned int * extra_certs_ret_len,
-                     gnutls_x509_crl_t * crl)
+                     gnutls_x509_crt_t ** chain,
+                     unsigned int * chain_len,
+                     gnutls_x509_crt_t ** extra_certs,
+                     unsigned int * extra_certs_len,
+                     gnutls_x509_crl_t * crl,
+                     unsigned int flags)
 {
   gnutls_pkcs12_bag_t bag = NULL;
-  gnutls_x509_crt_t *extra_certs = NULL;
-  unsigned int extra_certs_len = 0;
+  gnutls_x509_crt_t *_extra_certs = NULL;
+  unsigned int _extra_certs_len = 0;
+  gnutls_x509_crt_t *_chain = NULL;
+  unsigned int _chain_len = 0;
   int idx = 0;
   int ret;
   size_t cert_id_size = 0;
@@ -1394,9 +1439,10 @@ gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
   uint8_t key_id[20];
   int privkey_ok = 0;
 
-  *cert = NULL;
   *key = NULL;
-  *crl = NULL;
+  
+  if (crl)
+    *crl = NULL;
 
   /* find the first private key */
   for (;;)
@@ -1635,41 +1681,49 @@ gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
                 }
 
               if (memcmp (cert_id, key_id, cert_id_size) != 0)
-                {               /* they don't match - skip the certificate */
-                  if (extra_certs_ret)
+                { /* they don't match - skip the certificate */
+                  if (extra_certs)
                     {
-                      extra_certs = gnutls_realloc (extra_certs,
-                                                    sizeof(extra_certs[0]) *
-                                                    ++extra_certs_len);
-                      if (!extra_certs)
+                      _extra_certs = gnutls_realloc (_extra_certs,
+                                                     sizeof(_extra_certs[0]) *
+                                                     ++_extra_certs_len);
+                      if (!_extra_certs)
                         {
                           gnutls_assert ();
                           ret = GNUTLS_E_MEMORY_ERROR;
                           goto done;
                         }
-                      extra_certs[extra_certs_len - 1] = this_cert;
+                      _extra_certs[_extra_certs_len - 1] = this_cert;
                       this_cert = NULL;
                     }
                   else
                     {
                        gnutls_x509_crt_deinit (this_cert);
                     }
-                  break;
                 }
               else
                 {
-                   if (*cert != NULL)        /* no need to set it again */
-                     {
-                        gnutls_assert ();
-                        break;
-                     }
-                   *cert = this_cert;
-                   this_cert = NULL;
+                  if (_chain_len == 0)
+                    {
+                      _chain = gnutls_malloc (sizeof(_chain[0]) * (++_chain_len));
+                      if (!_chain)
+                        {
+                          gnutls_assert ();
+                          ret = GNUTLS_E_MEMORY_ERROR;
+                          goto done;
+                        }
+                      _chain[_chain_len - 1] = this_cert;
+                      this_cert = NULL;
+                    }
+                  else
+                    {
+                       gnutls_x509_crt_deinit (this_cert);
+                    }
                 }
               break;
 
             case GNUTLS_BAG_CRL:
-              if (*crl != NULL)
+              if (crl == NULL || *crl != NULL)
                 {
                   gnutls_assert ();
                   break;
@@ -1704,30 +1758,54 @@ gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
       gnutls_pkcs12_bag_deinit (bag);
     }
 
+  if (_chain_len != 1)
+    {
+      ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+      goto done;
+    }
+
+  ret = make_chain(&_chain, &_chain_len, &_extra_certs, &_extra_certs_len);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto done;
+    }
+
   ret = 0;
 
 done:
   if (bag)
     gnutls_pkcs12_bag_deinit (bag);
 
-  if (ret)
+  if (ret < 0)
     {
       if (*key)
         gnutls_x509_privkey_deinit(*key);
-      if (*cert)
-        gnutls_x509_crt_deinit(*cert);
-      if (extra_certs_len)
+      if (_extra_certs_len && _extra_certs != NULL)
+        {
+          unsigned int i;
+          for (i = 0; i < _extra_certs_len; i++)
+            gnutls_x509_crt_deinit(_extra_certs[i]);
+          gnutls_free(_extra_certs);
+        }
+      if (_chain_len && chain != NULL)
         {
           unsigned int i;
-          for (i = 0; i < extra_certs_len; i++)
-            gnutls_x509_crt_deinit(extra_certs[i]);
-          gnutls_free(extra_certs);
+          for (i = 0; i < _chain_len; i++)
+            gnutls_x509_crt_deinit(_chain[i]);
+          gnutls_free(_chain);
         }
     }
-  else if (extra_certs_ret)
+  else 
     {
-      *extra_certs_ret = extra_certs;
-      *extra_certs_ret_len = extra_certs_len;
+      if (extra_certs) 
+        {
+          *extra_certs = _extra_certs;
+          *extra_certs_len = _extra_certs_len;
+        }
+      
+      *chain = _chain;
+      *chain_len = _chain_len;
     }
 
   return ret;
index 8437924e5569e538c385a0b4fa83e03bcaa51c6c..70b343ab82ae8c9823da69ae7d1b181973b07ffa 100644 (file)
@@ -68,7 +68,7 @@ ctests = mini-deflate simple gc set_pkcs12_cred certder certuniqueid  \
         mini-rehandshake rng-fork mini-eagain-dtls resume-dtls \
         x509cert x509cert-tl infoaccess rsa-encrypt-decrypt \
         mini-loss-time mini-tdb mini-dtls-rehandshake mini-record \
-        mini-termination mini-x509-cas mini-x509-2
+        mini-termination mini-x509-cas mini-x509-2 pkcs12_simple
 
 if ENABLE_OCSP
 ctests += ocsp
@@ -100,6 +100,7 @@ TESTS = $(ctests) $(dist_check_SCRIPTS)
 
 TESTS_ENVIRONMENT =                                            \
        CAFILE=$(srcdir)/cert-tests/ca-certs.pem                \
+       PKCS12_MANY_CERTS_FILE=$(srcdir)/pkcs12-decode/pkcs12_5certs.p12        \
        PKCS12FILE=$(srcdir)/pkcs12-decode/client.p12           \
        PKCS12PASSWORD=foobar                                   \
        PKCS12FILE_2=$(srcdir)/pkcs12-decode/pkcs12_2certs.p12  \
index 4e9dd89b05a1d31720296e40d2b483c19dd960e3..e7fabcd262807a9c09c19964ea13fd04b8d56e8b 100644 (file)
@@ -19,7 +19,8 @@
 # along with this file; if not, write to the Free Software Foundation,
 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
-EXTRA_DIST = client.p12 noclient.p12 unclient.p12 pkcs12_2certs.p12
+EXTRA_DIST = client.p12 noclient.p12 unclient.p12 pkcs12_2certs.p12 \
+       pkcs12_5certs.p12
 
 dist_check_SCRIPTS = pkcs12
 
diff --git a/tests/pkcs12-decode/pkcs12_5certs.p12 b/tests/pkcs12-decode/pkcs12_5certs.p12
new file mode 100644 (file)
index 0000000..5fc9cd3
Binary files /dev/null and b/tests/pkcs12-decode/pkcs12_5certs.p12 differ
diff --git a/tests/pkcs12_simple.c b/tests/pkcs12_simple.c
new file mode 100644 (file)
index 0000000..ad17c0e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2005-2012 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <read-file.h>
+#include <gnutls/pkcs12.h>
+#include <gnutls/x509.h>
+#include "utils.h"
+
+void
+doit (void)
+{
+  const char *filename, *password = "1234";
+  gnutls_pkcs12_t pkcs12;
+  unsigned char* file_data;
+  size_t file_size;
+  gnutls_datum_t data;
+  gnutls_x509_crt_t * chain, * extras;
+  unsigned int chain_size, extras_size, i;
+  gnutls_x509_privkey_t pkey;
+  int ret;
+
+  ret = gnutls_global_init ();
+  if (ret < 0)
+    fail ("gnutls_global_init failed %d\n", ret);
+
+  ret = gnutls_pkcs12_init(&pkcs12);
+  if (ret < 0)
+    fail ("initialization failed: %s\n", gnutls_strerror(ret));
+
+  filename = getenv ("PKCS12_MANY_CERTS_FILE");
+
+  if (!filename)
+    filename = "pkcs12-decode/pkcs12_5certs.p12";
+
+  if (debug)
+    success ("Reading PKCS#12 blob from `%s' using password `%s'.\n",
+             filename, password);
+             
+  file_data = (void*)read_binary_file( filename, &file_size);
+  if (file_data == NULL)
+    fail("cannot open file");
+
+  data.data = file_data;
+  data.size = file_size;
+  ret = gnutls_pkcs12_import(pkcs12, &data, GNUTLS_X509_FMT_DER, 0);
+  if (ret < 0)
+    fail ("pkcs12_import failed %d: %s\n", ret, gnutls_strerror (ret));
+
+  free(file_data);
+
+  if (debug)
+    success ("Read file OK\n");
+
+  ret = gnutls_pkcs12_simple_parse (pkcs12, password, &pkey, &chain, &chain_size,
+                                    &extras, &extras_size, NULL, 0);
+  if (ret < 0)
+    fail ("pkcs12_simple_parse failed %d: %s\n", ret, gnutls_strerror (ret));
+
+  if (chain_size != 1)
+    fail("chain size (%u) should have been 1\n", chain_size);
+
+  if (extras_size != 4)
+    fail("extras size (%u) should have been 4\n", extras_size);
+  
+  if (debug)
+    {
+      char dn[512];
+      size_t dn_size;
+    
+      dn_size = sizeof(dn);
+      ret = gnutls_x509_crt_get_dn(chain[0], dn, &dn_size);
+      if (ret < 0)
+        fail ("crt_get_dn failed %d: %s\n", ret, gnutls_strerror (ret));
+
+      success("dn: %s\n", dn);
+
+      dn_size = sizeof(dn);
+      ret = gnutls_x509_crt_get_issuer_dn(chain[0], dn, &dn_size);
+      if (ret < 0)
+        fail ("crt_get_dn failed %d: %s\n", ret, gnutls_strerror (ret));
+
+      success("issuer dn: %s\n", dn);
+    }
+  
+  gnutls_pkcs12_deinit(pkcs12);
+  gnutls_x509_privkey_deinit(pkey);
+
+  for (i=0;i<chain_size;i++)
+    gnutls_x509_crt_deinit(chain[i]);
+  gnutls_free(chain);
+
+  for (i=0;i<extras_size;i++)
+    gnutls_x509_crt_deinit(extras[i]);
+  gnutls_free(extras);
+
+  gnutls_global_deinit ();
+}