diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-06-09 11:02:07 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-06-09 11:47:46 +0200 |
commit | 6c82bf34969ba76c19a8dbda1239d48e552e6bc1 (patch) | |
tree | 6f92b345028f9ea41aa5e57dc61f6837bf165125 | |
parent | 2576a9d933e4f29f69a7182faa9c4210eeec8fee (diff) | |
download | gnutls-6c82bf34969ba76c19a8dbda1239d48e552e6bc1.tar.gz |
Changed prototype for gnutls_pkcs12_simple_parse() to simplify chain building.
-rw-r--r-- | lib/gnutls_x509.c | 62 | ||||
-rw-r--r-- | lib/includes/gnutls/pkcs12.h | 17 | ||||
-rw-r--r-- | lib/x509/pkcs12.c | 156 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/pkcs12-decode/Makefile.am | 3 | ||||
-rw-r--r-- | tests/pkcs12-decode/pkcs12_5certs.p12 | bin | 0 -> 7338 bytes | |||
-rw-r--r-- | tests/pkcs12_simple.c | 121 |
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 Binary files differnew file mode 100644 index 0000000000..5fc9cd397d --- /dev/null +++ b/tests/pkcs12-decode/pkcs12_5certs.p12 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 (); +} |