Implement certificate matching for TPM/PKCS#11 privkeys
authorDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 13 Jun 2012 15:38:14 +0000 (16:38 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 13 Jun 2012 15:38:14 +0000 (16:38 +0100)
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
gnutls.c

index c4d8141..4abb0fc 100644 (file)
--- 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);