Resolves: #1195771 - implement public key pinning for NSS backend

This commit is contained in:
Kamil Dudka 2015-04-22 15:01:09 +02:00
parent 167643f9ce
commit 54e48b0348
2 changed files with 313 additions and 0 deletions

View File

@ -0,0 +1,308 @@
From b5425b256cc9db483e3e4365c5ba6121c86d9031 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Wed, 22 Apr 2015 14:47:26 +0200
Subject: [PATCH 1/2] docs: distribute the CURLOPT_PINNEDPUBLICKEY(3) man page,
too
Upstream-commit: ba4741842e6be654906da8a022e0e866acdd1557
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 | 65 +++++++++++++++++++++++++++++
docs/libcurl/opts/Makefile.am | 9 ++--
docs/libcurl/opts/Makefile.in | 9 ++--
3 files changed, 77 insertions(+), 6 deletions(-)
create mode 100644 docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
new file mode 100644
index 0000000..2d86392
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3
@@ -0,0 +1,65 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" *
+.\" * This software is licensed as described in the file COPYING, which
+.\" * you should have received as part of this distribution. The terms
+.\" * are also available at http://curl.haxx.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_PINNEDPUBLICKEY 3 "27 Aug 2014" "libcurl 7.38.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_PINNEDPUBLICKEY \- set pinned public key
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PINNEDPUBLICKEY, char *pinnedpubkey);
+.SH DESCRIPTION
+Pass a pointer to a zero terminated string as parameter. The string should be
+the file name of your pinned public key. The format expected is "PEM" or "DER".
+
+When negotiating a TLS or SSL connection, the server sends a certificate
+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.
+.SH DEFAULT
+NULL
+.SH PROTOCOLS
+All TLS based protocols: HTTPS, FTPS, IMAPS, POP3, SMTPS etc.
+.SH EXAMPLE
+.nf
+CURL *curl = curl_easy_init();
+if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+ curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, "/etc/publickey.der");
+
+ /* Perform the request */
+ curl_easy_perform(curl);
+}
+.fi
+.SH AVAILABILITY
+If built TLS enabled. This is currently only implemented in the OpenSSL,
+GnuTLS and GSKit backends.
+
+Added in libcurl 7.39.0
+.SH RETURN VALUE
+Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
+CURLE_OUT_OF_MEMORY if there was insufficient heap space.
+.SH "SEE ALSO"
+.BR CURLOPT_SSL_VERIFYPEER "(3), "
+.BR CURLOPT_SSL_VERIFYHOST "(3), "
+.BR CURLOPT_CAINFO "(3), "
+.BR CURLOPT_CAPATH "(3), "
diff --git a/docs/libcurl/opts/Makefile.am b/docs/libcurl/opts/Makefile.am
index e032a25..c4332e9 100644
--- a/docs/libcurl/opts/Makefile.am
+++ b/docs/libcurl/opts/Makefile.am
@@ -66,7 +66,8 @@ man_MANS = CURLOPT_ACCEPT_ENCODING.3 CURLOPT_ACCEPTTIMEOUT_MS.3 \
CURLOPT_NEW_DIRECTORY_PERMS.3 CURLOPT_NEW_FILE_PERMS.3 \
CURLOPT_NOBODY.3 CURLOPT_NOPROGRESS.3 CURLOPT_NOPROXY.3 \
CURLOPT_NOSIGNAL.3 CURLOPT_OPENSOCKETDATA.3 \
- CURLOPT_OPENSOCKETFUNCTION.3 CURLOPT_PASSWORD.3 CURLOPT_PORT.3 \
+ CURLOPT_OPENSOCKETFUNCTION.3 CURLOPT_PASSWORD.3 \
+ CURLOPT_PINNEDPUBLICKEY.3 CURLOPT_PORT.3 \
CURLOPT_POST.3 CURLOPT_POSTFIELDS.3 CURLOPT_POSTFIELDSIZE.3 \
CURLOPT_POSTFIELDSIZE_LARGE.3 CURLOPT_POSTQUOTE.3 CURLOPT_POSTREDIR.3 \
CURLOPT_PREQUOTE.3 CURLOPT_PRIVATE.3 CURLOPT_PROGRESSDATA.3 \
@@ -167,7 +168,8 @@ HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
CURLOPT_NEW_FILE_PERMS.html CURLOPT_NOBODY.html \
CURLOPT_NOPROGRESS.html CURLOPT_NOPROXY.html CURLOPT_NOSIGNAL.html \
CURLOPT_OPENSOCKETDATA.html CURLOPT_OPENSOCKETFUNCTION.html \
- CURLOPT_PASSWORD.html CURLOPT_PORT.html CURLOPT_POST.html \
+ CURLOPT_PASSWORD.html CURLOPT_PINNEDPUBLICKEY.html CURLOPT_PORT.html \
+ CURLOPT_POST.html \
CURLOPT_POSTFIELDS.html CURLOPT_POSTFIELDSIZE.html \
CURLOPT_POSTFIELDSIZE_LARGE.html CURLOPT_POSTQUOTE.html \
CURLOPT_POSTREDIR.html CURLOPT_PREQUOTE.html CURLOPT_PRIVATE.html \
@@ -270,7 +272,8 @@ PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
CURLOPT_NEW_DIRECTORY_PERMS.pdf CURLOPT_NEW_FILE_PERMS.pdf \
CURLOPT_NOBODY.pdf CURLOPT_NOPROGRESS.pdf CURLOPT_NOPROXY.pdf \
CURLOPT_NOSIGNAL.pdf CURLOPT_OPENSOCKETDATA.pdf \
- CURLOPT_OPENSOCKETFUNCTION.pdf CURLOPT_PASSWORD.pdf CURLOPT_PORT.pdf \
+ CURLOPT_OPENSOCKETFUNCTION.pdf CURLOPT_PASSWORD.pdf \
+ CURLOPT_PINNEDPUBLICKEY.pdf CURLOPT_PORT.pdf \
CURLOPT_POST.pdf CURLOPT_POSTFIELDS.pdf CURLOPT_POSTFIELDSIZE.pdf \
CURLOPT_POSTFIELDSIZE_LARGE.pdf CURLOPT_POSTQUOTE.pdf \
CURLOPT_POSTREDIR.pdf CURLOPT_PREQUOTE.pdf CURLOPT_PRIVATE.pdf \
diff --git a/docs/libcurl/opts/Makefile.in b/docs/libcurl/opts/Makefile.in
index 07d29cb..ceaa1bb 100644
--- a/docs/libcurl/opts/Makefile.in
+++ b/docs/libcurl/opts/Makefile.in
@@ -411,7 +411,8 @@ man_MANS = CURLOPT_ACCEPT_ENCODING.3 CURLOPT_ACCEPTTIMEOUT_MS.3 \
CURLOPT_NEW_DIRECTORY_PERMS.3 CURLOPT_NEW_FILE_PERMS.3 \
CURLOPT_NOBODY.3 CURLOPT_NOPROGRESS.3 CURLOPT_NOPROXY.3 \
CURLOPT_NOSIGNAL.3 CURLOPT_OPENSOCKETDATA.3 \
- CURLOPT_OPENSOCKETFUNCTION.3 CURLOPT_PASSWORD.3 CURLOPT_PORT.3 \
+ CURLOPT_OPENSOCKETFUNCTION.3 CURLOPT_PASSWORD.3 \
+ CURLOPT_PINNEDPUBLICKEY.3 CURLOPT_PORT.3 \
CURLOPT_POST.3 CURLOPT_POSTFIELDS.3 CURLOPT_POSTFIELDSIZE.3 \
CURLOPT_POSTFIELDSIZE_LARGE.3 CURLOPT_POSTQUOTE.3 CURLOPT_POSTREDIR.3 \
CURLOPT_PREQUOTE.3 CURLOPT_PRIVATE.3 CURLOPT_PROGRESSDATA.3 \
@@ -512,7 +513,8 @@ HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
CURLOPT_NEW_FILE_PERMS.html CURLOPT_NOBODY.html \
CURLOPT_NOPROGRESS.html CURLOPT_NOPROXY.html CURLOPT_NOSIGNAL.html \
CURLOPT_OPENSOCKETDATA.html CURLOPT_OPENSOCKETFUNCTION.html \
- CURLOPT_PASSWORD.html CURLOPT_PORT.html CURLOPT_POST.html \
+ CURLOPT_PASSWORD.html CURLOPT_PINNEDPUBLICKEY.html CURLOPT_PORT.html \
+ CURLOPT_POST.html \
CURLOPT_POSTFIELDS.html CURLOPT_POSTFIELDSIZE.html \
CURLOPT_POSTFIELDSIZE_LARGE.html CURLOPT_POSTQUOTE.html \
CURLOPT_POSTREDIR.html CURLOPT_PREQUOTE.html CURLOPT_PRIVATE.html \
@@ -615,7 +617,8 @@ PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
CURLOPT_NEW_DIRECTORY_PERMS.pdf CURLOPT_NEW_FILE_PERMS.pdf \
CURLOPT_NOBODY.pdf CURLOPT_NOPROGRESS.pdf CURLOPT_NOPROXY.pdf \
CURLOPT_NOSIGNAL.pdf CURLOPT_OPENSOCKETDATA.pdf \
- CURLOPT_OPENSOCKETFUNCTION.pdf CURLOPT_PASSWORD.pdf CURLOPT_PORT.pdf \
+ CURLOPT_OPENSOCKETFUNCTION.pdf CURLOPT_PASSWORD.pdf \
+ CURLOPT_PINNEDPUBLICKEY.pdf CURLOPT_PORT.pdf \
CURLOPT_POST.pdf CURLOPT_POSTFIELDS.pdf CURLOPT_POSTFIELDSIZE.pdf \
CURLOPT_POSTFIELDSIZE_LARGE.pdf CURLOPT_POSTQUOTE.pdf \
CURLOPT_POSTREDIR.pdf CURLOPT_PREQUOTE.pdf CURLOPT_PRIVATE.pdf \
--
2.3.5
From e5b6de7f78806f82dee0c5359e18d904e56836c6 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Wed, 25 Mar 2015 13:48:41 +0100
Subject: [PATCH 2/2] nss: implement public key pinning for NSS backend
Bug: https://bugzilla.redhat.com/1195771
Upstream-commit: b47c17d67c9b5c9e985375b090f0140bf43cb146
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
docs/curl.1 | 3 +-
docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 | 2 +-
lib/vtls/nss.c | 53 +++++++++++++++++++++++++++++
src/tool_help.c | 2 +-
tests/runtests.pl | 1 +
5 files changed, 58 insertions(+), 3 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/src/tool_help.c b/src/tool_help.c
index bb7aa7c..27638ef 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -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 "
diff --git a/tests/runtests.pl b/tests/runtests.pl
index ef9d3c8..b64c423 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|wolfssl)/i) {
--
2.3.5

View File

@ -7,6 +7,9 @@ Group: Applications/Internet
Source: http://curl.haxx.se/download/%{name}-%{version}.tar.lzma
Source2: curlbuild.h
# implement public key pinning for NSS backend (#1195771)
Patch1: 0001-curl-7.42.0-b47c17d6.patch
# patch making libcurl multilib ready
Patch101: 0101-curl-7.32.0-multilib.patch
@ -115,6 +118,7 @@ documentation of the library, too.
%setup -q
# upstream patches
%patch1 -p1
# Fedora patches
%patch101 -p1
@ -239,6 +243,7 @@ rm -rf $RPM_BUILD_ROOT
* Wed Apr 22 2015 Kamil Dudka <kdudka@redhat.com> 7.42.0-1
- new upstream release (fixes CVE-2015-3143, CVE-2015-3144, CVE-2015-3145,
and CVE-2015-3148)
- implement public key pinning for NSS backend (#1195771)
* Wed Feb 25 2015 Kamil Dudka <kdudka@redhat.com> 7.41.0-1
- new upstream release