From 54e48b034848cce523c59e76f5648cc06464a549 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 22 Apr 2015 15:01:09 +0200 Subject: [PATCH] Resolves: #1195771 - implement public key pinning for NSS backend --- 0001-curl-7.42.0-b47c17d6.patch | 308 ++++++++++++++++++++++++++++++++ curl.spec | 5 + 2 files changed, 313 insertions(+) create mode 100644 0001-curl-7.42.0-b47c17d6.patch diff --git a/0001-curl-7.42.0-b47c17d6.patch b/0001-curl-7.42.0-b47c17d6.patch new file mode 100644 index 0000000..e9979ae --- /dev/null +++ b/0001-curl-7.42.0-b47c17d6.patch @@ -0,0 +1,308 @@ +From b5425b256cc9db483e3e4365c5ba6121c86d9031 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +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 +--- + 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, , 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 ++ ++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 +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 +--- + 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 + #include + #include ++#include /* 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 + diff --git a/curl.spec b/curl.spec index 9293d88..a9693eb 100644 --- a/curl.spec +++ b/curl.spec @@ -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 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 7.41.0-1 - new upstream release