static int load_pkcs12_certificate(struct openconnect_info *vpninfo,
gnutls_datum_t *datum,
gnutls_x509_privkey_t *key,
- gnutls_x509_crt_t *cert,
+ gnutls_x509_crt_t **chain,
+ unsigned int *chain_len,
gnutls_x509_crt_t **extra_certs,
- unsigned int *nr_extra_certs,
+ unsigned int *extra_certs_len,
gnutls_x509_crl_t *crl)
{
gnutls_pkcs12_t p12;
return ret;
}
- err = gnutls_pkcs12_simple_parse(vpninfo->https_cred, p12, pass, key,
- cert, extra_certs, nr_extra_certs, crl);
+ err = gnutls_pkcs12_simple_parse(p12, pass, key, chain, chain_len,
+ extra_certs, extra_certs_len, crl, 0);
gnutls_pkcs12_deinit(p12);
if (err) {
vpn_progress(vpninfo, PRG_ERR,
gnutls_x509_crl_t crl = NULL;
gnutls_x509_crt_t last_cert, cert = NULL;
gnutls_x509_crt_t *extra_certs = NULL, *supporting_certs = NULL;
- unsigned int nr_supporting_certs, nr_extra_certs = 0;
+ unsigned int nr_supporting_certs = 0, nr_extra_certs = 0;
+ unsigned int certs_to_free = 0; /* How many of supporting_certs */
int err; /* GnuTLS error */
int ret = 0; /* our error (zero or -errno) */
int i;
if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
- ret = load_pkcs12_certificate(vpninfo, &fdata, &key, &cert,
- &extra_certs, &nr_extra_certs, &crl);
+ ret = load_pkcs12_certificate(vpninfo, &fdata, &key,
+ &supporting_certs, &nr_supporting_certs,
+ &extra_certs, &nr_extra_certs,
+ &crl);
if (ret < 0)
goto out;
- else if (!ret)
- goto got_cert;
+ else if (!ret) {
+ if (nr_supporting_certs) {
+ cert = supporting_certs[0];
+ goto got_cert;
+ }
+ vpn_progress(vpninfo, PRG_ERR,
+ _("PKCS#11 file contained no certificate\n"));
+ ret = -EINVAL;
+ goto out;
+ }
/* It returned NOT_PKCS12.
Fall through to try PEM formats. */
multiple certs with the same name, it doesn't necessarily
choose the _right_ one. (RT#1942)
Pick the right ones for ourselves and add them manually. */
- last_cert = cert;
- nr_supporting_certs = 1; /* Our starting cert */
+
+ if (nr_supporting_certs) {
+ /* We already got a bunch of certs from PKCS#12 file.
+ Remember how many need to be freed when we're done,
+ since we'll expand the supporting_certs array with
+ more from the cafile if we can. */
+ last_cert = supporting_certs[nr_supporting_certs-1];
+ certs_to_free = nr_supporting_certs;
+ } else {
+ last_cert = cert;
+ certs_to_free = nr_supporting_certs = 1;
+ }
while (1) {
gnutls_x509_crt_t issuer;
}
/* OK, we found a new cert to add to our chain. */
- supporting_certs = realloc(supporting_certs,
- sizeof(cert) * ++nr_supporting_certs);
+ supporting_certs = gnutls_realloc(supporting_certs,
+ sizeof(cert) * ++nr_supporting_certs);
if (!supporting_certs) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to allocate memory for supporting certificates\n"));
/* The world is probably about to end, but try without them anyway */
- break;
+ certs_to_free = 0;
+ ret = -ENOMEM;
+ goto out;
}
/* First time we actually allocated an array? Copy the first cert into it */
gnutls_x509_privkey_deinit(key);
if (cert)
gnutls_x509_crt_deinit(cert);
+ /* From 1 because cert is the first one (and might exist
+ even if supporting_certs is NULL) */
+ for (i = 1; i < certs_to_free; i++) {
+ if (supporting_certs[i])
+ gnutls_x509_crt_deinit(supporting_certs[i]);
+ }
for (i = 0; i < nr_extra_certs; i++) {
if (extra_certs[i])
gnutls_x509_crt_deinit(extra_certs[i]);
}
- free(extra_certs);
- free(supporting_certs);
+ gnutls_free(extra_certs);
+ gnutls_free(supporting_certs);
gnutls_free(fdata.data);
return ret;
}
* This is (now) gnutls_pkcs12_simple_parse() from GnuTLS 3.1, although
* it was actually taken from parse_pkcs12() in GnuTLS 2.12.x (where it
* was under LGPLv2.1) and modified locally. The modifications were
- * accepted back into GnuTLS in commit 9a43e8fa.
+ * accepted back into GnuTLS in commit 9a43e8fa. Further modifications
+ * by Nikos Mavrogiannopoulos are included here under LGPLv2.1 with his
+ * explicit permission.
*/
+
#define opaque unsigned char
#define gnutls_assert() do {} while(0)
+#define gnutls_assert_val(x) (x)
/*
* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
*/
+/* 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.
+ * @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_len: will be updated with the number of additional
+ * certs.
+ * @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 chain, and any additional
+ * certificates and a CRL.
+ *
+ * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
+ * and both may be set to %NULL. If either is non-%NULL, then both must
+ * be.
+ *
+ * MAC:ed PKCS#12 files are supported. Encrypted PKCS#12 bags are
+ * supported. Encrypted PKCS#8 private keys are supported. However,
+ * only password based security, and the same password for all
+ * operations, are supported.
+ *
+ * The private keys may be RSA PKCS#1 or DSA private keys encoded in
+ * the OpenSSL way.
+ *
+ * PKCS#12 file may contain many keys and/or certificates, and there
+ * is no way to identify which key/certificate pair you want. You
+ * should make sure the PKCS#12 file only contain one key/certificate
+ * pair and/or one CRL.
+ *
+ * It is believed that the limitations of this function is acceptable
+ * for most usage, and that any more flexibility would introduce
+ * complexity that would make it harder to use this functionality at
+ * all.
+ *
+ * If the provided structure has encrypted fields but no password
+ * is provided then this function returns %GNUTLS_E_DECRYPTION_FAILED.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1
+ **/
static int
-gnutls_pkcs12_simple_parse (gnutls_certificate_credentials_t res,
- 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_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_t bag = NULL;
- gnutls_x509_crt_t *extra_certs = NULL;
- 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;
opaque key_id[20];
int privkey_ok = 0;
- *cert = NULL;
*key = NULL;
- *crl = NULL;
+
+ if (crl)
+ *crl = NULL;
/* find the first private key */
for (;;)
if (ret == GNUTLS_BAG_ENCRYPTED)
{
+ if (password == NULL)
+ {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
ret = gnutls_pkcs12_bag_decrypt (bag, password);
if (ret < 0)
{
switch (type)
{
case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ if (password == NULL)
+ {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
case GNUTLS_BAG_PKCS8_KEY:
if (*key != NULL) /* too simple to continue */
{
}
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;
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)
{
- int i;
- for (i = 0; i < extra_certs_len; i++)
- gnutls_x509_crt_deinit(extra_certs[i]);
- gnutls_free(extra_certs);
+ unsigned int i;
+ 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;