nss: implement public key pinning for NSS backend
Bug: https://bugzilla.redhat.com/1195771
This commit is contained in:
parent
1fd33e3ec8
commit
b47c17d67c
@ -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
|
if it does not exactly match the public key provided to this option, curl will
|
||||||
abort the connection before sending or receiving any data.
|
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.
|
If this option is used several times, the last one will be used.
|
||||||
(Added in 7.39.0)
|
(Added in 7.39.0)
|
||||||
|
@ -52,7 +52,7 @@ if(curl) {
|
|||||||
.fi
|
.fi
|
||||||
.SH AVAILABILITY
|
.SH AVAILABILITY
|
||||||
If built TLS enabled. This is currently only implemented in the OpenSSL,
|
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
|
Added in libcurl 7.39.0
|
||||||
.SH RETURN VALUE
|
.SH RETURN VALUE
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include <base64.h>
|
#include <base64.h>
|
||||||
#include <cert.h>
|
#include <cert.h>
|
||||||
#include <prerror.h>
|
#include <prerror.h>
|
||||||
|
#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */
|
||||||
|
|
||||||
#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
|
#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
|
||||||
|
|
||||||
@ -943,6 +944,53 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
|
|||||||
return res;
|
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.
|
* 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;
|
return CURLE_OK;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -156,7 +156,7 @@ static const char *const helptext[] = {
|
|||||||
" --pass PASS Pass phrase for the private key (SSL/SSH)",
|
" --pass PASS Pass phrase for the private key (SSL/SSH)",
|
||||||
" --path-as-is Do not squash .. sequences in URL path",
|
" --path-as-is Do not squash .. sequences in URL path",
|
||||||
" --pinnedpubkey FILE Public key (PEM/DER) to verify peer against "
|
" --pinnedpubkey FILE Public key (PEM/DER) to verify peer against "
|
||||||
"(OpenSSL/GnuTLS/GSKit only)",
|
"(OpenSSL/GnuTLS/NSS/GSKit only)",
|
||||||
" --post301 "
|
" --post301 "
|
||||||
"Do not switch to GET after following a 301 redirect (H)",
|
"Do not switch to GET after following a 301 redirect (H)",
|
||||||
" --post302 "
|
" --post302 "
|
||||||
|
@ -2346,6 +2346,7 @@ sub checksystem {
|
|||||||
}
|
}
|
||||||
elsif ($libcurl =~ /nss/i) {
|
elsif ($libcurl =~ /nss/i) {
|
||||||
$has_nss=1;
|
$has_nss=1;
|
||||||
|
$has_sslpinning=1;
|
||||||
$ssllib="NSS";
|
$ssllib="NSS";
|
||||||
}
|
}
|
||||||
elsif ($libcurl =~ /(yassl|wolfssl)/i) {
|
elsif ($libcurl =~ /(yassl|wolfssl)/i) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user