|
@ -13,6 +13,10 @@ |
|
|
#include "util.h" |
|
|
#include "util.h" |
|
|
#include "util-inl.h" |
|
|
#include "util-inl.h" |
|
|
#include "v8.h" |
|
|
#include "v8.h" |
|
|
|
|
|
// CNNIC Hash WhiteList is taken from
|
|
|
|
|
|
// https://hg.mozilla.org/mozilla-central/raw-file/98820360ab66/security/
|
|
|
|
|
|
// certverifier/CNNICHashWhitelist.inc
|
|
|
|
|
|
#include "CNNICHashWhitelist.inc" |
|
|
|
|
|
|
|
|
#include <errno.h> |
|
|
#include <errno.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdlib.h> |
|
@ -84,6 +88,34 @@ using v8::V8; |
|
|
using v8::Value; |
|
|
using v8::Value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Subject DER of CNNIC ROOT CA and CNNIC EV ROOT CA are taken from
|
|
|
|
|
|
// https://hg.mozilla.org/mozilla-central/file/98820360ab66/security/
|
|
|
|
|
|
// certverifier/NSSCertDBTrustDomain.cpp#l672
|
|
|
|
|
|
// C = CN, O = CNNIC, CN = CNNIC ROOT
|
|
|
|
|
|
static const uint8_t CNNIC_ROOT_CA_SUBJECT_DATA[] = |
|
|
|
|
|
"\x30\x32\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x0E\x30" |
|
|
|
|
|
"\x0C\x06\x03\x55\x04\x0A\x13\x05\x43\x4E\x4E\x49\x43\x31\x13\x30\x11\x06" |
|
|
|
|
|
"\x03\x55\x04\x03\x13\x0A\x43\x4E\x4E\x49\x43\x20\x52\x4F\x4F\x54"; |
|
|
|
|
|
static const uint8_t* cnnic_p = CNNIC_ROOT_CA_SUBJECT_DATA; |
|
|
|
|
|
static X509_NAME* cnnic_name = |
|
|
|
|
|
d2i_X509_NAME(nullptr, &cnnic_p, sizeof(CNNIC_ROOT_CA_SUBJECT_DATA)-1); |
|
|
|
|
|
|
|
|
|
|
|
// C = CN, O = China Internet Network Information Center, CN = China
|
|
|
|
|
|
// Internet Network Information Center EV Certificates Root
|
|
|
|
|
|
static const uint8_t CNNIC_EV_ROOT_CA_SUBJECT_DATA[] = |
|
|
|
|
|
"\x30\x81\x8A\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x32" |
|
|
|
|
|
"\x30\x30\x06\x03\x55\x04\x0A\x0C\x29\x43\x68\x69\x6E\x61\x20\x49\x6E\x74" |
|
|
|
|
|
"\x65\x72\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F" |
|
|
|
|
|
"\x72\x6D\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x31\x47\x30\x45" |
|
|
|
|
|
"\x06\x03\x55\x04\x03\x0C\x3E\x43\x68\x69\x6E\x61\x20\x49\x6E\x74\x65\x72" |
|
|
|
|
|
"\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F\x72\x6D" |
|
|
|
|
|
"\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x20\x45\x56\x20\x43\x65" |
|
|
|
|
|
"\x72\x74\x69\x66\x69\x63\x61\x74\x65\x73\x20\x52\x6F\x6F\x74"; |
|
|
|
|
|
static const uint8_t* cnnic_ev_p = CNNIC_EV_ROOT_CA_SUBJECT_DATA; |
|
|
|
|
|
static X509_NAME *cnnic_ev_name = |
|
|
|
|
|
d2i_X509_NAME(nullptr, &cnnic_ev_p, |
|
|
|
|
|
sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA)-1); |
|
|
|
|
|
|
|
|
// Forcibly clear OpenSSL's error stack on return. This stops stale errors
|
|
|
// Forcibly clear OpenSSL's error stack on return. This stops stale errors
|
|
|
// from popping up later in the lifecycle of crypto operations where they
|
|
|
// from popping up later in the lifecycle of crypto operations where they
|
|
|
// would cause spurious failures. It's a rather blunt method, though.
|
|
|
// would cause spurious failures. It's a rather blunt method, though.
|
|
@ -2210,49 +2242,91 @@ void Connection::Initialize(Environment* env, Handle<Object> target) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { |
|
|
inline int compar(const void* a, const void* b) { |
|
|
// Quoting SSL_set_verify(3ssl):
|
|
|
return memcmp(a, b, CNNIC_WHITELIST_HASH_LEN); |
|
|
//
|
|
|
} |
|
|
// The VerifyCallback function is used to control the behaviour when
|
|
|
|
|
|
// the SSL_VERIFY_PEER flag is set. It must be supplied by the
|
|
|
|
|
|
// application and receives two arguments: preverify_ok indicates,
|
|
|
inline int IsSelfSigned(X509* cert) { |
|
|
// whether the verification of the certificate in question was passed
|
|
|
return X509_NAME_cmp(X509_get_subject_name(cert), |
|
|
// (preverify_ok=1) or not (preverify_ok=0). x509_ctx is a pointer to
|
|
|
X509_get_issuer_name(cert)) == 0; |
|
|
// the complete context used for the certificate chain verification.
|
|
|
} |
|
|
//
|
|
|
|
|
|
// The certificate chain is checked starting with the deepest nesting
|
|
|
|
|
|
// level (the root CA certificate) and worked upward to the peer's
|
|
|
inline X509* FindRoot(STACK_OF(X509)* sk) { |
|
|
// certificate. At each level signatures and issuer attributes are
|
|
|
for (int i = 0; i < sk_X509_num(sk); i++) { |
|
|
// checked. Whenever a verification error is found, the error number is
|
|
|
X509* cert = sk_X509_value(sk, i); |
|
|
// stored in x509_ctx and VerifyCallback is called with preverify_ok=0.
|
|
|
if (IsSelfSigned(cert)) |
|
|
// By applying X509_CTX_store_* functions VerifyCallback can locate the
|
|
|
return cert; |
|
|
// certificate in question and perform additional steps (see EXAMPLES).
|
|
|
} |
|
|
// If no error is found for a certificate, VerifyCallback is called
|
|
|
return nullptr; |
|
|
// with preverify_ok=1 before advancing to the next level.
|
|
|
} |
|
|
//
|
|
|
|
|
|
// The return value of VerifyCallback controls the strategy of the
|
|
|
|
|
|
// further verification process. If VerifyCallback returns 0, the
|
|
|
// Whitelist check for certs issued by CNNIC. See
|
|
|
// verification process is immediately stopped with "verification
|
|
|
// https://blog.mozilla.org/security/2015/04/02
|
|
|
// failed" state. If SSL_VERIFY_PEER is set, a verification failure
|
|
|
// /distrusting-new-cnnic-certificates/
|
|
|
// alert is sent to the peer and the TLS/SSL handshake is terminated. If
|
|
|
inline CheckResult CheckWhitelistedServerCert(X509_STORE_CTX* ctx) { |
|
|
// VerifyCallback returns 1, the verification process is continued. If
|
|
|
unsigned char hash[CNNIC_WHITELIST_HASH_LEN]; |
|
|
// VerifyCallback always returns 1, the TLS/SSL handshake will not be
|
|
|
unsigned int hashlen = CNNIC_WHITELIST_HASH_LEN; |
|
|
// terminated with respect to verification failures and the connection
|
|
|
|
|
|
// will be established. The calling process can however retrieve the
|
|
|
STACK_OF(X509)* chain = X509_STORE_CTX_get1_chain(ctx); |
|
|
// error code of the last verification error using
|
|
|
CHECK_NE(chain, nullptr); |
|
|
// SSL_get_verify_result(3) or by maintaining its own error storage
|
|
|
CHECK_GT(sk_X509_num(chain), 0); |
|
|
// managed by VerifyCallback.
|
|
|
|
|
|
//
|
|
|
// Take the last cert as root at the first time.
|
|
|
// If no VerifyCallback is specified, the default callback will be
|
|
|
X509* root_cert = sk_X509_value(chain, sk_X509_num(chain)-1); |
|
|
// used. Its return value is identical to preverify_ok, so that any
|
|
|
X509_NAME* root_name = X509_get_subject_name(root_cert); |
|
|
// verification failure will lead to a termination of the TLS/SSL
|
|
|
|
|
|
// handshake with an alert message, if SSL_VERIFY_PEER is set.
|
|
|
if (!IsSelfSigned(root_cert)) { |
|
|
//
|
|
|
root_cert = FindRoot(chain); |
|
|
// Since we cannot perform I/O quickly enough in this callback, we ignore
|
|
|
CHECK_NE(root_cert, nullptr); |
|
|
// all preverify_ok errors and let the handshake continue. It is
|
|
|
root_name = X509_get_subject_name(root_cert); |
|
|
// imparative that the user use Connection::VerifyError after the
|
|
|
} |
|
|
// 'secure' callback has been made.
|
|
|
|
|
|
return 1; |
|
|
// When the cert is issued from either CNNNIC ROOT CA or CNNNIC EV
|
|
|
|
|
|
// ROOT CA, check a hash of its leaf cert if it is in the whitelist.
|
|
|
|
|
|
if (X509_NAME_cmp(root_name, cnnic_name) == 0 || |
|
|
|
|
|
X509_NAME_cmp(root_name, cnnic_ev_name) == 0) { |
|
|
|
|
|
X509* leaf_cert = sk_X509_value(chain, 0); |
|
|
|
|
|
int ret = X509_digest(leaf_cert, EVP_sha256(), hash, |
|
|
|
|
|
&hashlen); |
|
|
|
|
|
CHECK(ret); |
|
|
|
|
|
|
|
|
|
|
|
void* result = bsearch(hash, WhitelistedCNNICHashes, |
|
|
|
|
|
ARRAY_SIZE(WhitelistedCNNICHashes), |
|
|
|
|
|
CNNIC_WHITELIST_HASH_LEN, compar); |
|
|
|
|
|
if (result == nullptr) { |
|
|
|
|
|
sk_X509_pop_free(chain, X509_free); |
|
|
|
|
|
return CHECK_CERT_REVOKED; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sk_X509_pop_free(chain, X509_free); |
|
|
|
|
|
return CHECK_OK; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { |
|
|
|
|
|
// Failure on verification of the cert is handled in
|
|
|
|
|
|
// Connection::VerifyError.
|
|
|
|
|
|
if (preverify_ok == 0) |
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
|
|
|
|
// Server does not need to check the whitelist.
|
|
|
|
|
|
SSL* ssl = static_cast<SSL*>( |
|
|
|
|
|
X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); |
|
|
|
|
|
|
|
|
|
|
|
if (SSL_is_server(ssl)) |
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
|
|
|
|
// Client needs to check if the server cert is listed in the
|
|
|
|
|
|
// whitelist when it is issued by the specific rootCAs.
|
|
|
|
|
|
CheckResult ret = CheckWhitelistedServerCert(ctx); |
|
|
|
|
|
if (ret == CHECK_CERT_REVOKED) |
|
|
|
|
|
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); |
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|