summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2012-06-09 11:02:07 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2012-06-09 11:47:46 +0200
commit6c82bf34969ba76c19a8dbda1239d48e552e6bc1 (patch)
tree6f92b345028f9ea41aa5e57dc61f6837bf165125
parent2576a9d933e4f29f69a7182faa9c4210eeec8fee (diff)
downloadgnutls-6c82bf34969ba76c19a8dbda1239d48e552e6bc1.tar.gz
Changed prototype for gnutls_pkcs12_simple_parse() to simplify chain building.
-rw-r--r--lib/gnutls_x509.c62
-rw-r--r--lib/includes/gnutls/pkcs12.h17
-rw-r--r--lib/x509/pkcs12.c156
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/pkcs12-decode/Makefile.am3
-rw-r--r--tests/pkcs12-decode/pkcs12_5certs.p12bin0 -> 7338 bytes
-rw-r--r--tests/pkcs12_simple.c121
7 files changed, 266 insertions, 96 deletions
diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c
index 96656d2472..62d547cc82 100644
--- a/lib/gnutls_x509.c
+++ b/lib/gnutls_x509.c
@@ -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)
diff --git a/lib/includes/gnutls/pkcs12.h b/lib/includes/gnutls/pkcs12.h
index dde5b84275..14b45f8ef3 100644
--- a/lib/includes/gnutls/pkcs12.h
+++ b/lib/includes/gnutls/pkcs12.h
@@ -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:
diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c
index 78e4075097..a595488b32 100644
--- a/lib/x509/pkcs12.c
+++ b/lib/x509/pkcs12.c
@@ -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;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8437924e55..70b343ab82 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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 \
diff --git a/tests/pkcs12-decode/Makefile.am b/tests/pkcs12-decode/Makefile.am
index 4e9dd89b05..e7fabcd262 100644
--- a/tests/pkcs12-decode/Makefile.am
+++ b/tests/pkcs12-decode/Makefile.am
@@ -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
index 0000000000..5fc9cd397d
--- /dev/null
+++ b/tests/pkcs12-decode/pkcs12_5certs.p12
Binary files differ
diff --git a/tests/pkcs12_simple.c b/tests/pkcs12_simple.c
new file mode 100644
index 0000000000..ad17c0e413
--- /dev/null
+++ b/tests/pkcs12_simple.c
@@ -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 ();
+}