cURL / Mailing Lists / curl-library / Single Mail

curl-library

[PATCH] nss: implement public key pinning for NSS backend

From: Kamil Dudka <kdudka_at_redhat.com>
Date: Wed, 25 Mar 2015 14:13:01 +0100

As the feature window is already closed, I am sending this just to get
some feedback on the patch, will merge it after the release eventually.

---
 docs/curl.1                                 |  3 +-
 docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 |  2 +-
 lib/vtls/nss.c                              | 53 +++++++++++++++++++++++++++++
 tests/runtests.pl                           |  1 +
 4 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/docs/curl.1 b/docs/curl.1
index 908f648..0e56715 100644
--- a/docs/curl.1
+++ b/docs/curl.1
@@ -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)
diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
index 2d86392..4cc68b1 100644
--- a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
+++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
@@ -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
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index feb00ca..daf12a9 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -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:
diff --git a/tests/runtests.pl b/tests/runtests.pl
index 1348751..7f67b97 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -2346,6 +2346,7 @@ sub checksystem {
            }
            elsif ($libcurl =~ /nss/i) {
                $has_nss=1;
+               $has_sslpinning=1;
                $ssllib="NSS";
            }
            elsif ($libcurl =~ /yassl/i) {
-- 
2.1.0
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html
Received on 2015-03-25