From 09bc61713235bc096e3e95436d817fefd10bc257 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 13 Jun 2012 16:38:14 +0100 Subject: [PATCH] Implement certificate matching for TPM/PKCS#11 privkeys Signed-off-by: David Woodhouse --- gnutls.c | 69 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/gnutls.c b/gnutls.c index c4d81417..4abb0fc7 100644 --- a/gnutls.c +++ b/gnutls.c @@ -948,27 +948,70 @@ static int load_certificate(struct openconnect_info *vpninfo) goto got_key; } } - /* We shouldn't reach this. It means that we didn't find *any* matching cert */ - vpn_progress(vpninfo, PRG_ERR, - _("No SSL certificate found to match private key\n")); - ret = -EINVAL; - goto out; + /* There's no pkey (there's an x509 key), so we'll fall straight through the + * bit at match_cert: below, and go directly to the bit where it prints the + * 'no match found' error and exits. */ #ifdef HAVE_GNUTLS_CERTIFICATE_SET_KEY match_cert: - if (!cert) { - /* FIXME: How do we check which cert matches the pkey? - For now we just assume that the first one in the list is the right one. */ - cert = extra_certs[0]; + /* We only get here if we have a key in pkey from PKCS#11 or TPM anyway, but + the check makes it clearer... and allows us to define some local variables. */ + if (pkey) { + gnutls_datum_t input; + gnutls_datum_t sig; + + input.data = (void *)&load_certificate; + input.size = 20; + + err = gnutls_privkey_sign_data(pkey, GNUTLS_DIG_SHA1, 0, + &input, &sig); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Error signing test data with private key: %s\n"), + gnutls_strerror(err)); + goto out; + } + + for (i=0; i < (extra_certs?nr_extra_certs:1); i++) { + gnutls_pubkey_t pubkey; + + gnutls_pubkey_init(&pubkey); + err = gnutls_pubkey_import_x509(pubkey, extra_certs?extra_certs[i]:cert, 0); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Error validating signature against certificate: %s\n"), + gnutls_strerror(err)); + /* We'll probably fail shortly if we don't find it. */ + gnutls_pubkey_deinit(pubkey); + continue; + } + err = gnutls_pubkey_verify_data(pubkey, 0, &input, &sig); + gnutls_pubkey_deinit(pubkey); - /* Move the rest of the array down */ - for (i = 0; i < nr_extra_certs - 1; i++) - extra_certs[i] = extra_certs[i+1]; + if (err >= 0) { + if (extra_certs) { + cert = extra_certs[i]; - nr_extra_certs--; + /* Move the rest of the array down */ + for (; i < nr_extra_certs - 1; i++) + extra_certs[i] = extra_certs[i+1]; + + nr_extra_certs--; + } + gnutls_free(sig.data); + goto got_key; + } + } + gnutls_free(sig.data); } #endif + /* We shouldn't reach this. It means that we didn't find *any* matching cert */ + vpn_progress(vpninfo, PRG_ERR, + _("No SSL certificate found to match private key\n")); + ret = -EINVAL; + goto out; + /********************************************************************/ got_key: /* Now we have both cert(s) and key, and we should be ready to go. */ check_certificate_expiry(vpninfo, cert);