]> granicus.if.org Git - curl/commitdiff
nss: implement public key pinning for NSS backend
authorKamil Dudka <kdudka@redhat.com>
Wed, 25 Mar 2015 12:48:41 +0000 (13:48 +0100)
committerKamil Dudka <kdudka@redhat.com>
Wed, 22 Apr 2015 11:21:31 +0000 (13:21 +0200)
Bug: https://bugzilla.redhat.com/1195771

docs/curl.1
docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
lib/vtls/nss.c
src/tool_help.c
tests/runtests.pl

index 908f648990bdd931bc26a6b8c72c19dc1e866dca..0e567159e85c852481f76fb51345d9458260ac41 100644 (file)
@@ -548,7 +548,8 @@ indicating its identity. A public key is extracted from this certificate and
 if it does not exactly match the public key provided to this option, curl will
 abort the connection before sending or receiving any data.
 
-This is currently only implemented in the OpenSSL, GnuTLS and GSKit backends.
+This is currently only implemented in the OpenSSL, GnuTLS, NSS and GSKit
+backends.
 
 If this option is used several times, the last one will be used.
 (Added in 7.39.0)
index 2d8639275a1acb8c0a7ae18ee066d8c03c7464bd..4cc68b1d336b348292f1ee9cf8d4ac9b02236f61 100644 (file)
@@ -52,7 +52,7 @@ if(curl) {
 .fi
 .SH AVAILABILITY
 If built TLS enabled. This is currently only implemented in the OpenSSL,
-GnuTLS and GSKit backends.
+GnuTLS, NSS and GSKit backends.
 
 Added in libcurl 7.39.0
 .SH RETURN VALUE
index feb00ca81d03dec2a143aacbafbbe541ae3294e2..daf12a9d7afafa83afa23e4af402d52a833e652d 100644 (file)
@@ -56,6 +56,7 @@
 #include <base64.h>
 #include <cert.h>
 #include <prerror.h>
+#include <keyhi.h>        /* for SECKEY_DestroyPublicKey() */
 
 #define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
 
@@ -943,6 +944,53 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
   return res;
 }
 
+static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
+                                const char *pinnedpubkey)
+{
+  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+  struct SessionHandle *data = connssl->data;
+  CERTCertificate *cert;
+
+  if(!pinnedpubkey)
+    /* no pinned public key specified */
+    return CURLE_OK;
+
+  /* get peer certificate */
+  cert = SSL_PeerCertificate(connssl->handle);
+  if(cert) {
+    /* extract public key from peer certificate */
+    SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert);
+    if(pubkey) {
+      /* encode the public key as DER */
+      SECItem *cert_der = PK11_DEREncodePublicKey(pubkey);
+      if(cert_der) {
+        /* compare the public key with the pinned public key */
+        result = Curl_pin_peer_pubkey(pinnedpubkey,
+                                      cert_der->data,
+                                      cert_der->len);
+        SECITEM_FreeItem(cert_der, PR_TRUE);
+      }
+      SECKEY_DestroyPublicKey(pubkey);
+    }
+    CERT_DestroyCertificate(cert);
+  }
+
+  /* report the resulting status */
+  switch(result) {
+  case CURLE_OK:
+    infof(data, "pinned public key verified successfully!\n");
+    break;
+  case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+    failf(data, "failed to verify pinned public key");
+    break;
+  default:
+    /* OOM, etc. */
+    break;
+  }
+
+  return result;
+}
+
 /**
  *
  * Callback to pick the SSL client certificate.
@@ -1806,6 +1854,11 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
     }
   }
 
+  result = cmp_peer_pubkey(connssl, data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+  if(result)
+    /* status already printed */
+    goto error;
+
   return CURLE_OK;
 
 error:
index bb7aa7c3ac264103c73d059f59fcfce5b9e73573..27638ef1662ae452aac78edd9044ea8406cfafb4 100644 (file)
@@ -156,7 +156,7 @@ static const char *const helptext[] = {
   "     --pass PASS     Pass phrase for the private key (SSL/SSH)",
   "     --path-as-is    Do not squash .. sequences in URL path",
   "     --pinnedpubkey FILE  Public key (PEM/DER) to verify peer against "
-  "(OpenSSL/GnuTLS/GSKit only)",
+  "(OpenSSL/GnuTLS/NSS/GSKit only)",
   "     --post301       "
   "Do not switch to GET after following a 301 redirect (H)",
   "     --post302       "
index ef9d3c8d4ab455f4066c343e621ab690f4dfce99..b64c42373267e3651ee6e97a8523161a4c9bb01e 100755 (executable)
@@ -2346,6 +2346,7 @@ sub checksystem {
            }
            elsif ($libcurl =~ /nss/i) {
                $has_nss=1;
+               $has_sslpinning=1;
                $ssllib="NSS";
            }
            elsif ($libcurl =~ /(yassl|wolfssl)/i) {