From 8cb9854f13cdde513c52242214cd2716af42982b Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Tue, 14 Aug 2018 17:13:39 -0400 Subject: [PATCH 1/2] src: merge SSLWrap with TLSWrap `SSLWrap` was introduced in b9a0eb0688104f3619e75e1b9a698080e10d1ec7, but the only other C++ class to "inherit" from `SSLWrap` went away in 9301b8a9c69d112b98c7d60e074c845d80342b4e. Since all of this API is already part of `TLSWrap`, move everything to that class. This also helps move a decent amount of functionality out of `node_crypto` to a "better" place within C++ land. --- src/node_crypto.cc | 1246 +------------------------------------------- src/node_crypto.h | 161 +----- src/tls_wrap.cc | 1197 +++++++++++++++++++++++++++++++++++++++++- src/tls_wrap.h | 112 +++- 4 files changed, 1321 insertions(+), 1395 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7f5544b8f22180..5597d2c2e7c008 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -29,7 +29,6 @@ #include "node_crypto_clienthello-inl.h" #include "node_mutex.h" #include "node_internals.h" -#include "tls_wrap.h" // TLSWrap #include "async_wrap-inl.h" #include "env-inl.h" @@ -52,11 +51,6 @@ static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1; static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----"; static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1; -static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL - | ASN1_STRFLGS_UTF8_CONVERT - | XN_FLAG_SEP_MULTILINE - | XN_FLAG_FN_SN; - namespace node { namespace crypto { @@ -65,9 +59,7 @@ using v8::Boolean; using v8::ConstructorBehavior; using v8::Context; using v8::DontDelete; -using v8::EscapableHandleScope; using v8::Exception; -using v8::External; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; @@ -91,24 +83,6 @@ using v8::Uint32; using v8::Value; -struct StackOfX509Deleter { - void operator()(STACK_OF(X509)* p) const { sk_X509_pop_free(p, X509_free); } -}; -using StackOfX509 = std::unique_ptr; - -struct StackOfXASN1Deleter { - void operator()(STACK_OF(ASN1_OBJECT)* p) const { - sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free); - } -}; -using StackOfASN1 = std::unique_ptr; - -// OPENSSL_free is a macro, so we need a wrapper function. -struct OpenSSLBufferDeleter { - void operator()(char* pointer) const { OPENSSL_free(pointer); } -}; -using OpenSSLBuffer = std::unique_ptr; - static const char* const root_certs[] = { #include "node_root_certs.h" // NOLINT(build/include_order) }; @@ -119,35 +93,6 @@ static std::string extra_root_certs_file; // NOLINT(runtime/string) static X509_STORE* root_cert_store; -// Just to generate static methods -template void SSLWrap::AddMethods(Environment* env, - Local t); -template void SSLWrap::ConfigureSecureContext(SecureContext* sc); -template void SSLWrap::SetSNIContext(SecureContext* sc); -template int SSLWrap::SetCACerts(SecureContext* sc); -template SSL_SESSION* SSLWrap::GetSessionCallback( - SSL* s, - const unsigned char* key, - int len, - int* copy); -template int SSLWrap::NewSessionCallback(SSL* s, - SSL_SESSION* sess); -template void SSLWrap::OnClientHello( - void* arg, - const ClientHelloParser::ClientHello& hello); -template int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg); -template void SSLWrap::DestroySSL(); -template int SSLWrap::SSLCertCallback(SSL* s, void* arg); -template void SSLWrap::WaitForCertCb(CertCb cb, void* arg); -template int SSLWrap::SelectALPNCallback( - SSL* s, - const unsigned char** out, - unsigned char* outlen, - const unsigned char* in, - unsigned int inlen, - void* arg); - - static int PasswordCallback(char* buf, int size, int rwflag, void* u) { if (u) { size_t buflen = static_cast(size); @@ -217,40 +162,41 @@ struct CryptoErrorVector : public std::vector { std::reverse(begin(), end()); } - inline Local ToException( + inline v8::Local ToException( Environment* env, - Local exception_string = Local()) const { + v8::Local exception_string = v8::Local()) const { if (exception_string.IsEmpty()) { CryptoErrorVector copy(*this); if (copy.empty()) copy.push_back("no error"); // But possibly a bug... // Use last element as the error message, everything else goes // into the .opensslErrorStack property on the exception object. auto exception_string = - String::NewFromUtf8(env->isolate(), copy.back().data(), - NewStringType::kNormal, copy.back().size()) + v8::String::NewFromUtf8(env->isolate(), copy.back().data(), + v8::NewStringType::kNormal, + copy.back().size()) .ToLocalChecked(); copy.pop_back(); return copy.ToException(env, exception_string); } - Local exception_v = Exception::Error(exception_string); + v8::Local exception_v = v8::Exception::Error(exception_string); CHECK(!exception_v.IsEmpty()); if (!empty()) { - Local array = Array::New(env->isolate(), size()); + v8::Local array = v8::Array::New(env->isolate(), size()); CHECK(!array.IsEmpty()); for (const std::string& string : *this) { const size_t index = &string - &front(); - Local value = - String::NewFromUtf8(env->isolate(), string.data(), - NewStringType::kNormal, string.size()) + v8::Local value = + v8::String::NewFromUtf8(env->isolate(), string.data(), + v8::NewStringType::kNormal, string.size()) .ToLocalChecked(); array->Set(env->context(), index, value).FromJust(); } CHECK(exception_v->IsObject()); - Local exception = exception_v.As(); + v8::Local exception = exception_v.As(); exception->Set(env->context(), env->openssl_error_stack(), array).FromJust(); } @@ -262,7 +208,7 @@ struct CryptoErrorVector : public std::vector { void ThrowCryptoError(Environment* env, unsigned long err, // NOLINT(runtime/int) - const char* message = nullptr) { + const char* message) { char message_buffer[128] = {0}; if (err != 0 || message == nullptr) { ERR_error_string_n(err, message_buffer, sizeof(message_buffer)); @@ -1373,1174 +1319,6 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { } -template -void SSLWrap::AddMethods(Environment* env, Local t) { - HandleScope scope(env->isolate()); - - env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate); - env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished); - env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished); - env->SetProtoMethodNoSideEffect(t, "getSession", GetSession); - env->SetProtoMethod(t, "setSession", SetSession); - env->SetProtoMethod(t, "loadSession", LoadSession); - env->SetProtoMethodNoSideEffect(t, "isSessionReused", IsSessionReused); - env->SetProtoMethodNoSideEffect(t, "verifyError", VerifyError); - env->SetProtoMethodNoSideEffect(t, "getCurrentCipher", GetCurrentCipher); - env->SetProtoMethod(t, "endParser", EndParser); - env->SetProtoMethod(t, "certCbDone", CertCbDone); - env->SetProtoMethod(t, "renegotiate", Renegotiate); - env->SetProtoMethodNoSideEffect(t, "getTLSTicket", GetTLSTicket); - env->SetProtoMethod(t, "newSessionDone", NewSessionDone); - env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); - env->SetProtoMethod(t, "requestOCSP", RequestOCSP); - env->SetProtoMethodNoSideEffect(t, "getEphemeralKeyInfo", - GetEphemeralKeyInfo); - env->SetProtoMethodNoSideEffect(t, "getProtocol", GetProtocol); - -#ifdef SSL_set_max_send_fragment - env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment); -#endif // SSL_set_max_send_fragment - - env->SetProtoMethodNoSideEffect(t, "getALPNNegotiatedProtocol", - GetALPNNegotiatedProto); - env->SetProtoMethod(t, "setALPNProtocols", SetALPNProtocols); -} - - -template -void SSLWrap::ConfigureSecureContext(SecureContext* sc) { - // OCSP stapling - SSL_CTX_set_tlsext_status_cb(sc->ctx_.get(), TLSExtStatusCallback); - SSL_CTX_set_tlsext_status_arg(sc->ctx_.get(), nullptr); -} - - -template -SSL_SESSION* SSLWrap::GetSessionCallback(SSL* s, - const unsigned char* key, - int len, - int* copy) { - Base* w = static_cast(SSL_get_app_data(s)); - - *copy = 0; - return w->next_sess_.release(); -} - - -template -int SSLWrap::NewSessionCallback(SSL* s, SSL_SESSION* sess) { - Base* w = static_cast(SSL_get_app_data(s)); - Environment* env = w->ssl_env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - if (!w->session_callbacks_) - return 0; - - // Check if session is small enough to be stored - int size = i2d_SSL_SESSION(sess, nullptr); - if (size > SecureContext::kMaxSessionSize) - return 0; - - // Serialize session - Local buff = Buffer::New(env, size).ToLocalChecked(); - unsigned char* serialized = reinterpret_cast( - Buffer::Data(buff)); - memset(serialized, 0, size); - i2d_SSL_SESSION(sess, &serialized); - - unsigned int session_id_length; - const unsigned char* session_id = SSL_SESSION_get_id(sess, - &session_id_length); - Local session = Buffer::Copy( - env, - reinterpret_cast(session_id), - session_id_length).ToLocalChecked(); - Local argv[] = { session, buff }; - w->new_session_wait_ = true; - w->MakeCallback(env->onnewsession_string(), arraysize(argv), argv); - - return 0; -} - - -template -void SSLWrap::OnClientHello(void* arg, - const ClientHelloParser::ClientHello& hello) { - Base* w = static_cast(arg); - Environment* env = w->ssl_env(); - HandleScope handle_scope(env->isolate()); - Local context = env->context(); - Context::Scope context_scope(context); - - Local hello_obj = Object::New(env->isolate()); - Local buff = Buffer::Copy( - env, - reinterpret_cast(hello.session_id()), - hello.session_size()).ToLocalChecked(); - hello_obj->Set(context, env->session_id_string(), buff).FromJust(); - if (hello.servername() == nullptr) { - hello_obj->Set(context, - env->servername_string(), - String::Empty(env->isolate())).FromJust(); - } else { - Local servername = OneByteString(env->isolate(), - hello.servername(), - hello.servername_size()); - hello_obj->Set(context, env->servername_string(), servername).FromJust(); - } - hello_obj->Set(context, - env->tls_ticket_string(), - Boolean::New(env->isolate(), hello.has_ticket())).FromJust(); - hello_obj->Set(context, - env->ocsp_request_string(), - Boolean::New(env->isolate(), hello.ocsp_request())).FromJust(); - - Local argv[] = { hello_obj }; - w->MakeCallback(env->onclienthello_string(), arraysize(argv), argv); -} - - -static bool SafeX509ExtPrint(BIO* out, X509_EXTENSION* ext) { - const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); - - if (method != X509V3_EXT_get_nid(NID_subject_alt_name)) - return false; - - GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); - if (names == nullptr) - return false; - - for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { - GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); - - if (i != 0) - BIO_write(out, ", ", 2); - - if (gen->type == GEN_DNS) { - ASN1_IA5STRING* name = gen->d.dNSName; - - BIO_write(out, "DNS:", 4); - BIO_write(out, name->data, name->length); - } else { - STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( - const_cast(method), gen, nullptr); - if (nval == nullptr) - return false; - X509V3_EXT_val_prn(out, nval, 0, 0); - sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); - } - } - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - - return true; -} - - -static void AddFingerprintDigest(const unsigned char* md, - unsigned int md_size, - char (*fingerprint)[3 * EVP_MAX_MD_SIZE + 1]) { - unsigned int i; - const char hex[] = "0123456789ABCDEF"; - - for (i = 0; i < md_size; i++) { - (*fingerprint)[3*i] = hex[(md[i] & 0xf0) >> 4]; - (*fingerprint)[(3*i)+1] = hex[(md[i] & 0x0f)]; - (*fingerprint)[(3*i)+2] = ':'; - } - - if (md_size > 0) { - (*fingerprint)[(3*(md_size-1))+2] = '\0'; - } else { - (*fingerprint)[0] = '\0'; - } -} - -static Local X509ToObject(Environment* env, X509* cert) { - EscapableHandleScope scope(env->isolate()); - Local context = env->context(); - Local info = Object::New(env->isolate()); - - BIOPointer bio(BIO_new(BIO_s_mem())); - BUF_MEM* mem; - if (X509_NAME_print_ex(bio.get(), - X509_get_subject_name(cert), - 0, - X509_NAME_FLAGS) > 0) { - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->subject_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - } - USE(BIO_reset(bio.get())); - - X509_NAME* issuer_name = X509_get_issuer_name(cert); - if (X509_NAME_print_ex(bio.get(), issuer_name, 0, X509_NAME_FLAGS) > 0) { - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->issuer_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - } - USE(BIO_reset(bio.get())); - - int nids[] = { NID_subject_alt_name, NID_info_access }; - Local keys[] = { env->subjectaltname_string(), - env->infoaccess_string() }; - CHECK_EQ(arraysize(nids), arraysize(keys)); - for (size_t i = 0; i < arraysize(nids); i++) { - int index = X509_get_ext_by_NID(cert, nids[i], -1); - if (index < 0) - continue; - - X509_EXTENSION* ext; - int rv; - - ext = X509_get_ext(cert, index); - CHECK_NOT_NULL(ext); - - if (!SafeX509ExtPrint(bio.get(), ext)) { - rv = X509V3_EXT_print(bio.get(), ext, 0, 0); - CHECK_EQ(rv, 1); - } - - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, keys[i], - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - - USE(BIO_reset(bio.get())); - } - - EVPKeyPointer pkey(X509_get_pubkey(cert)); - RSAPointer rsa; - if (pkey) - rsa.reset(EVP_PKEY_get1_RSA(pkey.get())); - - if (rsa) { - const BIGNUM* n; - const BIGNUM* e; - RSA_get0_key(rsa.get(), &n, &e, nullptr); - BN_print(bio.get(), n); - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->modulus_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - USE(BIO_reset(bio.get())); - - uint64_t exponent_word = static_cast(BN_get_word(e)); - uint32_t lo = static_cast(exponent_word); - uint32_t hi = static_cast(exponent_word >> 32); - if (hi == 0) { - BIO_printf(bio.get(), "0x%x", lo); - } else { - BIO_printf(bio.get(), "0x%x%08x", hi, lo); - } - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->exponent_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - USE(BIO_reset(bio.get())); - - int size = i2d_RSA_PUBKEY(rsa.get(), nullptr); - CHECK_GE(size, 0); - Local pubbuff = Buffer::New(env, size).ToLocalChecked(); - unsigned char* pubserialized = - reinterpret_cast(Buffer::Data(pubbuff)); - i2d_RSA_PUBKEY(rsa.get(), &pubserialized); - info->Set(env->pubkey_string(), pubbuff); - } - - pkey.reset(); - rsa.reset(); - - ASN1_TIME_print(bio.get(), X509_get_notBefore(cert)); - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->valid_from_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - USE(BIO_reset(bio.get())); - - ASN1_TIME_print(bio.get(), X509_get_notAfter(cert)); - BIO_get_mem_ptr(bio.get(), &mem); - info->Set(context, env->valid_to_string(), - String::NewFromUtf8(env->isolate(), mem->data, - NewStringType::kNormal, - mem->length).ToLocalChecked()).FromJust(); - bio.reset(); - - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int md_size; - char fingerprint[EVP_MAX_MD_SIZE * 3 + 1]; - if (X509_digest(cert, EVP_sha1(), md, &md_size)) { - AddFingerprintDigest(md, md_size, &fingerprint); - info->Set(context, env->fingerprint_string(), - OneByteString(env->isolate(), fingerprint)).FromJust(); - } - if (X509_digest(cert, EVP_sha256(), md, &md_size)) { - AddFingerprintDigest(md, md_size, &fingerprint); - info->Set(context, env->fingerprint256_string(), - OneByteString(env->isolate(), fingerprint)).FromJust(); - } - - StackOfASN1 eku(static_cast( - X509_get_ext_d2i(cert, NID_ext_key_usage, nullptr, nullptr))); - if (eku) { - Local ext_key_usage = Array::New(env->isolate()); - char buf[256]; - - int j = 0; - for (int i = 0; i < sk_ASN1_OBJECT_num(eku.get()); i++) { - if (OBJ_obj2txt(buf, - sizeof(buf), - sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { - ext_key_usage->Set(context, - j++, - OneByteString(env->isolate(), buf)).FromJust(); - } - } - - eku.reset(); - info->Set(context, env->ext_key_usage_string(), ext_key_usage).FromJust(); - } - - if (ASN1_INTEGER* serial_number = X509_get_serialNumber(cert)) { - BignumPointer bn(ASN1_INTEGER_to_BN(serial_number, nullptr)); - if (bn) { - OpenSSLBuffer buf(BN_bn2hex(bn.get())); - if (buf) { - info->Set(context, env->serial_number_string(), - OneByteString(env->isolate(), buf.get())).FromJust(); - } - } - } - - // Raw DER certificate - int size = i2d_X509(cert, nullptr); - Local buff = Buffer::New(env, size).ToLocalChecked(); - unsigned char* serialized = reinterpret_cast( - Buffer::Data(buff)); - i2d_X509(cert, &serialized); - info->Set(context, env->raw_string(), buff).FromJust(); - - return scope.Escape(info); -} - - -static Local AddIssuerChainToObject(X509Pointer* cert, - Local object, - StackOfX509&& peer_certs, - Environment* const env) { - Local context = env->isolate()->GetCurrentContext(); - cert->reset(sk_X509_delete(peer_certs.get(), 0)); - for (;;) { - int i; - for (i = 0; i < sk_X509_num(peer_certs.get()); i++) { - X509* ca = sk_X509_value(peer_certs.get(), i); - if (X509_check_issued(ca, cert->get()) != X509_V_OK) - continue; - - Local ca_info = X509ToObject(env, ca); - object->Set(context, env->issuercert_string(), ca_info).FromJust(); - object = ca_info; - - // NOTE: Intentionally freeing cert that is not used anymore. - // Delete cert and continue aggregating issuers. - cert->reset(sk_X509_delete(peer_certs.get(), i)); - break; - } - - // Issuer not found, break out of the loop. - if (i == sk_X509_num(peer_certs.get())) - break; - } - return object; -} - - -static StackOfX509 CloneSSLCerts(X509Pointer&& cert, - const STACK_OF(X509)* const ssl_certs) { - StackOfX509 peer_certs(sk_X509_new(nullptr)); - if (cert) - sk_X509_push(peer_certs.get(), cert.release()); - for (int i = 0; i < sk_X509_num(ssl_certs); i++) { - X509Pointer cert(X509_dup(sk_X509_value(ssl_certs, i))); - if (!cert || !sk_X509_push(peer_certs.get(), cert.get())) - return StackOfX509(); - // `cert` is now managed by the stack. - cert.release(); - } - return peer_certs; -} - - -static Local GetLastIssuedCert(X509Pointer* cert, - const SSLPointer& ssl, - Local issuer_chain, - Environment* const env) { - Local context = env->isolate()->GetCurrentContext(); - while (X509_check_issued(cert->get(), cert->get()) != X509_V_OK) { - X509* ca; - if (SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl.get()), cert->get(), &ca) <= 0) - break; - - Local ca_info = X509ToObject(env, ca); - issuer_chain->Set(context, env->issuercert_string(), ca_info).FromJust(); - issuer_chain = ca_info; - - // Delete previous cert and continue aggregating issuers. - cert->reset(ca); - } - return issuer_chain; -} - - -template -void SSLWrap::GetPeerCertificate( - const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->ssl_env(); - - ClearErrorOnReturn clear_error_on_return; - - Local result; - // Used to build the issuer certificate chain. - Local issuer_chain; - - // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` - // contains the `peer_certificate`, but on server it doesn't. - X509Pointer cert( - w->is_server() ? SSL_get_peer_certificate(w->ssl_.get()) : nullptr); - STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(w->ssl_.get()); - if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) - goto done; - - // Short result requested. - if (args.Length() < 1 || !args[0]->IsTrue()) { - result = X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); - goto done; - } - - if (auto peer_certs = CloneSSLCerts(std::move(cert), ssl_certs)) { - // First and main certificate. - X509Pointer cert(sk_X509_value(peer_certs.get(), 0)); - CHECK(cert); - result = X509ToObject(env, cert.release()); - - issuer_chain = - AddIssuerChainToObject(&cert, result, std::move(peer_certs), env); - issuer_chain = GetLastIssuedCert(&cert, w->ssl_, issuer_chain, env); - // Last certificate should be self-signed. - if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) - issuer_chain->Set(env->context(), - env->issuercert_string(), - issuer_chain).FromJust(); - } - - done: - if (result.IsEmpty()) - result = Object::New(env->isolate()); - args.GetReturnValue().Set(result); -} - - -template -void SSLWrap::GetFinished(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - // We cannot just pass nullptr to SSL_get_finished() - // because it would further be propagated to memcpy(), - // where the standard requirements as described in ISO/IEC 9899:2011 - // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. - // Thus, we use a dummy byte. - char dummy[1]; - size_t len = SSL_get_finished(w->ssl_.get(), dummy, sizeof dummy); - if (len == 0) - return; - - char* buf = Malloc(len); - CHECK_EQ(len, SSL_get_finished(w->ssl_.get(), buf, len)); - args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked()); -} - - -template -void SSLWrap::GetPeerFinished(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - // We cannot just pass nullptr to SSL_get_peer_finished() - // because it would further be propagated to memcpy(), - // where the standard requirements as described in ISO/IEC 9899:2011 - // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. - // Thus, we use a dummy byte. - char dummy[1]; - size_t len = SSL_get_peer_finished(w->ssl_.get(), dummy, sizeof dummy); - if (len == 0) - return; - - char* buf = Malloc(len); - CHECK_EQ(len, SSL_get_peer_finished(w->ssl_.get(), buf, len)); - args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked()); -} - - -template -void SSLWrap::GetSession(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - SSL_SESSION* sess = SSL_get_session(w->ssl_.get()); - if (sess == nullptr) - return; - - int slen = i2d_SSL_SESSION(sess, nullptr); - CHECK_GT(slen, 0); - - char* sbuf = Malloc(slen); - unsigned char* p = reinterpret_cast(sbuf); - i2d_SSL_SESSION(sess, &p); - args.GetReturnValue().Set(Buffer::New(env, sbuf, slen).ToLocalChecked()); -} - - -template -void SSLWrap::SetSession(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - if (args.Length() < 1) { - return THROW_ERR_MISSING_ARGS(env, "Session argument is mandatory"); - } - - THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session"); - size_t slen = Buffer::Length(args[0]); - std::vector sbuf(slen); - if (char* p = Buffer::Data(args[0])) - sbuf.assign(p, p + slen); - - const unsigned char* p = reinterpret_cast(sbuf.data()); - SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, slen)); - - if (sess == nullptr) - return; - - int r = SSL_set_session(w->ssl_.get(), sess.get()); - - if (!r) - return env->ThrowError("SSL_set_session error"); -} - - -template -void SSLWrap::LoadSession(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - if (args.Length() >= 1 && Buffer::HasInstance(args[0])) { - ssize_t slen = Buffer::Length(args[0]); - char* sbuf = Buffer::Data(args[0]); - - const unsigned char* p = reinterpret_cast(sbuf); - SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, slen); - - // Setup next session and move hello to the BIO buffer - w->next_sess_.reset(sess); - } -} - - -template -void SSLWrap::IsSessionReused(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - bool yes = SSL_session_reused(w->ssl_.get()); - args.GetReturnValue().Set(yes); -} - - -template -void SSLWrap::EndParser(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - w->hello_parser_.End(); -} - - -template -void SSLWrap::Renegotiate(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - ClearErrorOnReturn clear_error_on_return; - - bool yes = SSL_renegotiate(w->ssl_.get()) == 1; - args.GetReturnValue().Set(yes); -} - - -template -void SSLWrap::GetTLSTicket(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->ssl_env(); - - SSL_SESSION* sess = SSL_get_session(w->ssl_.get()); - if (sess == nullptr) - return; - - const unsigned char* ticket; - size_t length; - SSL_SESSION_get0_ticket(sess, &ticket, &length); - - if (ticket == nullptr) - return; - - Local buff = Buffer::Copy( - env, reinterpret_cast(ticket), length).ToLocalChecked(); - - args.GetReturnValue().Set(buff); -} - - -template -void SSLWrap::NewSessionDone(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - w->new_session_wait_ = false; - w->NewSessionDoneCb(); -} - - -template -void SSLWrap::SetOCSPResponse(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->env(); - - if (args.Length() < 1) - return THROW_ERR_MISSING_ARGS(env, "OCSP response argument is mandatory"); - - THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response"); - - w->ocsp_response_.Reset(args.GetIsolate(), args[0].As()); -} - - -template -void SSLWrap::RequestOCSP(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - SSL_set_tlsext_status_type(w->ssl_.get(), TLSEXT_STATUSTYPE_ocsp); -} - - -template -void SSLWrap::GetEphemeralKeyInfo( - const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - - CHECK(w->ssl_); - - // tmp key is available on only client - if (w->is_server()) - return args.GetReturnValue().SetNull(); - - Local info = Object::New(env->isolate()); - - EVP_PKEY* key; - - if (SSL_get_server_tmp_key(w->ssl_.get(), &key)) { - int kid = EVP_PKEY_id(key); - switch (kid) { - case EVP_PKEY_DH: - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).FromJust(); - info->Set(context, env->size_string(), - Integer::New(env->isolate(), EVP_PKEY_bits(key))).FromJust(); - break; - case EVP_PKEY_EC: - // TODO(shigeki) Change this to EVP_PKEY_X25519 and add EVP_PKEY_X448 - // after upgrading to 1.1.1. - case NID_X25519: - { - const char* curve_name; - if (kid == EVP_PKEY_EC) { - EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - curve_name = OBJ_nid2sn(nid); - EC_KEY_free(ec); - } else { - curve_name = OBJ_nid2sn(kid); - } - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH")).FromJust(); - info->Set(context, env->name_string(), - OneByteString(args.GetIsolate(), - curve_name)).FromJust(); - info->Set(context, env->size_string(), - Integer::New(env->isolate(), - EVP_PKEY_bits(key))).FromJust(); - } - break; - } - EVP_PKEY_free(key); - } - - return args.GetReturnValue().Set(info); -} - - -#ifdef SSL_set_max_send_fragment -template -void SSLWrap::SetMaxSendFragment( - const FunctionCallbackInfo& args) { - CHECK(args.Length() >= 1 && args[0]->IsNumber()); - - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - int rv = SSL_set_max_send_fragment( - w->ssl_.get(), - args[0]->Int32Value(w->ssl_env()->context()).FromJust()); - args.GetReturnValue().Set(rv); -} -#endif // SSL_set_max_send_fragment - - -template -void SSLWrap::VerifyError(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - // XXX(bnoordhuis) The UNABLE_TO_GET_ISSUER_CERT error when there is no - // peer certificate is questionable but it's compatible with what was - // here before. - long x509_verify_error = // NOLINT(runtime/int) - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; - if (X509* peer_cert = SSL_get_peer_certificate(w->ssl_.get())) { - X509_free(peer_cert); - x509_verify_error = SSL_get_verify_result(w->ssl_.get()); - } - - if (x509_verify_error == X509_V_OK) - return args.GetReturnValue().SetNull(); - - const char* reason = X509_verify_cert_error_string(x509_verify_error); - const char* code = reason; -#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; - switch (x509_verify_error) { - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) - CASE_X509_ERR(UNABLE_TO_GET_CRL) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) - CASE_X509_ERR(CERT_SIGNATURE_FAILURE) - CASE_X509_ERR(CRL_SIGNATURE_FAILURE) - CASE_X509_ERR(CERT_NOT_YET_VALID) - CASE_X509_ERR(CERT_HAS_EXPIRED) - CASE_X509_ERR(CRL_NOT_YET_VALID) - CASE_X509_ERR(CRL_HAS_EXPIRED) - CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) - CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) - CASE_X509_ERR(OUT_OF_MEM) - CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) - CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) - CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) - CASE_X509_ERR(CERT_CHAIN_TOO_LONG) - CASE_X509_ERR(CERT_REVOKED) - CASE_X509_ERR(INVALID_CA) - CASE_X509_ERR(PATH_LENGTH_EXCEEDED) - CASE_X509_ERR(INVALID_PURPOSE) - CASE_X509_ERR(CERT_UNTRUSTED) - CASE_X509_ERR(CERT_REJECTED) - } -#undef CASE_X509_ERR - - Isolate* isolate = args.GetIsolate(); - Local reason_string = OneByteString(isolate, reason); - Local exception_value = Exception::Error(reason_string); - Local exception_object = exception_value->ToObject(isolate); - exception_object->Set(w->env()->context(), w->env()->code_string(), - OneByteString(isolate, code)).FromJust(); - args.GetReturnValue().Set(exception_object); -} - - -template -void SSLWrap::GetCurrentCipher(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->ssl_env(); - Local context = env->context(); - - const SSL_CIPHER* c = SSL_get_current_cipher(w->ssl_.get()); - if (c == nullptr) - return; - - Local info = Object::New(env->isolate()); - const char* cipher_name = SSL_CIPHER_get_name(c); - info->Set(context, env->name_string(), - OneByteString(args.GetIsolate(), cipher_name)).FromJust(); - info->Set(context, env->version_string(), - OneByteString(args.GetIsolate(), "TLSv1/SSLv3")).FromJust(); - args.GetReturnValue().Set(info); -} - - -template -void SSLWrap::GetProtocol(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - const char* tls_version = SSL_get_version(w->ssl_.get()); - args.GetReturnValue().Set(OneByteString(args.GetIsolate(), tls_version)); -} - - -template -int SSLWrap::SelectALPNCallback(SSL* s, - const unsigned char** out, - unsigned char* outlen, - const unsigned char* in, - unsigned int inlen, - void* arg) { - Base* w = static_cast(SSL_get_app_data(s)); - Environment* env = w->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local alpn_buffer = - w->object()->GetPrivate( - env->context(), - env->alpn_buffer_private_symbol()).ToLocalChecked(); - CHECK(Buffer::HasInstance(alpn_buffer)); - const unsigned char* alpn_protos = - reinterpret_cast(Buffer::Data(alpn_buffer)); - unsigned alpn_protos_len = Buffer::Length(alpn_buffer); - int status = SSL_select_next_proto(const_cast(out), outlen, - alpn_protos, alpn_protos_len, in, inlen); - // According to 3.2. Protocol Selection of RFC7301, fatal - // no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not - // support it yet. See - // https://rt.openssl.org/Ticket/Display.html?id=3463&user=guest&pass=guest - return status == OPENSSL_NPN_NEGOTIATED ? SSL_TLSEXT_ERR_OK - : SSL_TLSEXT_ERR_NOACK; -} - - -template -void SSLWrap::GetALPNNegotiatedProto( - const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - - const unsigned char* alpn_proto; - unsigned int alpn_proto_len; - - SSL_get0_alpn_selected(w->ssl_.get(), &alpn_proto, &alpn_proto_len); - - if (!alpn_proto) - return args.GetReturnValue().Set(false); - - args.GetReturnValue().Set( - OneByteString(args.GetIsolate(), alpn_proto, alpn_proto_len)); -} - - -template -void SSLWrap::SetALPNProtocols(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->env(); - if (args.Length() < 1 || !Buffer::HasInstance(args[0])) - return env->ThrowTypeError("Must give a Buffer as first argument"); - - if (w->is_client()) { - const unsigned char* alpn_protos = - reinterpret_cast(Buffer::Data(args[0])); - unsigned alpn_protos_len = Buffer::Length(args[0]); - int r = SSL_set_alpn_protos(w->ssl_.get(), alpn_protos, alpn_protos_len); - CHECK_EQ(r, 0); - } else { - CHECK( - w->object()->SetPrivate( - env->context(), - env->alpn_buffer_private_symbol(), - args[0]).FromJust()); - // Server should select ALPN protocol from list of advertised by client - SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(w->ssl_.get()), - SelectALPNCallback, - nullptr); - } -} - - -template -int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg) { - Base* w = static_cast(SSL_get_app_data(s)); - Environment* env = w->env(); - HandleScope handle_scope(env->isolate()); - - if (w->is_client()) { - // Incoming response - const unsigned char* resp; - int len = SSL_get_tlsext_status_ocsp_resp(s, &resp); - Local arg; - if (resp == nullptr) { - arg = Null(env->isolate()); - } else { - arg = - Buffer::Copy(env, reinterpret_cast(resp), len) - .ToLocalChecked(); - } - - w->MakeCallback(env->onocspresponse_string(), 1, &arg); - - // Somehow, client is expecting different return value here - return 1; - } else { - // Outgoing response - if (w->ocsp_response_.IsEmpty()) - return SSL_TLSEXT_ERR_NOACK; - - Local obj = PersistentToLocal(env->isolate(), w->ocsp_response_); - char* resp = Buffer::Data(obj); - size_t len = Buffer::Length(obj); - - // OpenSSL takes control of the pointer after accepting it - char* data = node::Malloc(len); - memcpy(data, resp, len); - - if (!SSL_set_tlsext_status_ocsp_resp(s, data, len)) - free(data); - w->ocsp_response_.Reset(); - - return SSL_TLSEXT_ERR_OK; - } -} - - -template -void SSLWrap::WaitForCertCb(CertCb cb, void* arg) { - cert_cb_ = cb; - cert_cb_arg_ = arg; -} - - -template -int SSLWrap::SSLCertCallback(SSL* s, void* arg) { - Base* w = static_cast(SSL_get_app_data(s)); - - if (!w->is_server()) - return 1; - - if (!w->is_waiting_cert_cb()) - return 1; - - if (w->cert_cb_running_) - return -1; - - Environment* env = w->env(); - Local context = env->context(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(context); - w->cert_cb_running_ = true; - - Local info = Object::New(env->isolate()); - - const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); - if (servername == nullptr) { - info->Set(context, - env->servername_string(), - String::Empty(env->isolate())).FromJust(); - } else { - Local str = OneByteString(env->isolate(), servername, - strlen(servername)); - info->Set(context, env->servername_string(), str).FromJust(); - } - - const bool ocsp = (SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); - info->Set(context, env->ocsp_request_string(), - Boolean::New(env->isolate(), ocsp)).FromJust(); - - Local argv[] = { info }; - w->MakeCallback(env->oncertcb_string(), arraysize(argv), argv); - - if (!w->cert_cb_running_) - return 1; - - // Performing async action, wait... - return -1; -} - - -template -void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { - Base* w; - ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->env(); - - CHECK(w->is_waiting_cert_cb() && w->cert_cb_running_); - - Local object = w->object(); - Local ctx = object->Get(env->sni_context_string()); - Local cons = env->secure_context_constructor_template(); - - // Not an object, probably undefined or null - if (!ctx->IsObject()) - goto fire_cb; - - if (cons->HasInstance(ctx)) { - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, ctx.As()); - w->sni_context_.Reset(env->isolate(), ctx); - - int rv; - - // NOTE: reference count is not increased by this API methods - X509* x509 = SSL_CTX_get0_certificate(sc->ctx_.get()); - EVP_PKEY* pkey = SSL_CTX_get0_privatekey(sc->ctx_.get()); - STACK_OF(X509)* chain; - - rv = SSL_CTX_get0_chain_certs(sc->ctx_.get(), &chain); - if (rv) - rv = SSL_use_certificate(w->ssl_.get(), x509); - if (rv) - rv = SSL_use_PrivateKey(w->ssl_.get(), pkey); - if (rv && chain != nullptr) - rv = SSL_set1_chain(w->ssl_.get(), chain); - if (rv) - rv = w->SetCACerts(sc); - if (!rv) { - unsigned long err = ERR_get_error(); // NOLINT(runtime/int) - if (!err) - return env->ThrowError("CertCbDone"); - return ThrowCryptoError(env, err); - } - } else { - // Failure: incorrect SNI context object - Local err = Exception::TypeError(env->sni_context_err_string()); - w->MakeCallback(env->onerror_string(), 1, &err); - return; - } - - fire_cb: - CertCb cb; - void* arg; - - cb = w->cert_cb_; - arg = w->cert_cb_arg_; - - w->cert_cb_running_ = false; - w->cert_cb_ = nullptr; - w->cert_cb_arg_ = nullptr; - - cb(arg); -} - - -template -void SSLWrap::DestroySSL() { - if (!ssl_) - return; - - env_->isolate()->AdjustAmountOfExternalAllocatedMemory(-kExternalSize); - ssl_.reset(); -} - - -template -void SSLWrap::SetSNIContext(SecureContext* sc) { - ConfigureSecureContext(sc); - CHECK_EQ(SSL_set_SSL_CTX(ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); - - SetCACerts(sc); -} - - -template -int SSLWrap::SetCACerts(SecureContext* sc) { - int err = SSL_set1_verify_cert_store(ssl_.get(), - SSL_CTX_get_cert_store(sc->ctx_.get())); - if (err != 1) - return err; - - STACK_OF(X509_NAME)* list = SSL_dup_CA_list( - SSL_CTX_get_client_CA_list(sc->ctx_.get())); - - // NOTE: `SSL_set_client_CA_list` takes the ownership of `list` - SSL_set_client_CA_list(ssl_.get(), list); - return 1; -} - -int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { - // Quoting SSL_set_verify(3ssl): - // - // 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, - // whether the verification of the certificate in question was passed - // (preverify_ok=1) or not (preverify_ok=0). x509_ctx is a pointer to - // 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 - // certificate. At each level signatures and issuer attributes are - // checked. Whenever a verification error is found, the error number is - // stored in x509_ctx and VerifyCallback is called with preverify_ok=0. - // By applying X509_CTX_store_* functions VerifyCallback can locate the - // certificate in question and perform additional steps (see EXAMPLES). - // If no error is found for a certificate, VerifyCallback is called - // 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 - // verification process is immediately stopped with "verification - // failed" state. If SSL_VERIFY_PEER is set, a verification failure - // alert is sent to the peer and the TLS/SSL handshake is terminated. If - // VerifyCallback returns 1, the verification process is continued. If - // VerifyCallback always returns 1, the TLS/SSL handshake will not be - // terminated with respect to verification failures and the connection - // will be established. The calling process can however retrieve the - // error code of the last verification error using - // SSL_get_verify_result(3) or by maintaining its own error storage - // managed by VerifyCallback. - // - // If no VerifyCallback is specified, the default callback will be - // used. Its return value is identical to preverify_ok, so that any - // verification failure will lead to a termination of the TLS/SSL - // handshake with an alert message, if SSL_VERIFY_PEER is set. - // - // Since we cannot perform I/O quickly enough in this callback, we ignore - // all preverify_ok errors and let the handshake continue. It is - // imparative that the user use Connection::VerifyError after the - // 'secure' callback has been made. - return 1; -} - void CipherBase::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(New); diff --git a/src/node_crypto.h b/src/node_crypto.h index 269bccbc03a1d7..cdac934f56560a 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -75,7 +75,6 @@ struct MarkPopErrorOnReturn { using X509Pointer = DeleteFnPtr; using BIOPointer = DeleteFnPtr; using SSLCtxPointer = DeleteFnPtr; -using SSLSessionPointer = DeleteFnPtr; using SSLPointer = DeleteFnPtr; using EVPKeyPointer = DeleteFnPtr; using EVPKeyCtxPointer = DeleteFnPtr; @@ -88,15 +87,33 @@ using ECPointPointer = DeleteFnPtr; using ECKeyPointer = DeleteFnPtr; using DHPointer = DeleteFnPtr; +struct StackOfX509Deleter { + void operator()(STACK_OF(X509)* p) const { sk_X509_pop_free(p, X509_free); } +}; +using StackOfX509 = std::unique_ptr; + +struct StackOfXASN1Deleter { + void operator()(STACK_OF(ASN1_OBJECT)* p) const { + sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free); + } +}; +using StackOfASN1 = std::unique_ptr; + +// OPENSSL_free is a macro, so we need a wrapper function. +struct OpenSSLBufferDeleter { + void operator()(char* pointer) const { OPENSSL_free(pointer); } +}; +using OpenSSLBuffer = std::unique_ptr; + enum CheckResult { CHECK_CERT_REVOKED = 0, CHECK_OK = 1 }; -extern int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx); - extern void UseExtraCaCerts(const std::string& file); - +void ThrowCryptoError(Environment* env, + unsigned long err, // NOLINT(runtime/int) + const char* message = nullptr); void InitCryptoOnce(); class SecureContext : public BaseObject { @@ -205,142 +222,6 @@ class SecureContext : public BaseObject { } }; -// SSLWrap implicitly depends on the inheriting class' handle having an -// internal pointer to the Base class. -template -class SSLWrap { - public: - enum Kind { - kClient, - kServer - }; - - SSLWrap(Environment* env, SecureContext* sc, Kind kind) - : env_(env), - kind_(kind), - next_sess_(nullptr), - session_callbacks_(false), - new_session_wait_(false), - cert_cb_(nullptr), - cert_cb_arg_(nullptr), - cert_cb_running_(false) { - ssl_.reset(SSL_new(sc->ctx_.get())); - CHECK(ssl_); - env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); - } - - virtual ~SSLWrap() { - DestroySSL(); - } - - inline void enable_session_callbacks() { session_callbacks_ = true; } - inline bool is_server() const { return kind_ == kServer; } - inline bool is_client() const { return kind_ == kClient; } - inline bool is_waiting_new_session() const { return new_session_wait_; } - inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } - - protected: - typedef void (*CertCb)(void* arg); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - // Size allocated by OpenSSL: one for SSL structure, one for SSL3_STATE and - // some for buffers. - // NOTE: Actually it is much more than this - static const int64_t kExternalSize = - sizeof(SSL) + sizeof(SSL3_STATE) + 42 * 1024; -#else - // OpenSSL 1.1.0 has opaque structures. This is an estimate based on the size - // as of OpenSSL 1.1.0f. - static const int64_t kExternalSize = 4448 + 1024 + 42 * 1024; -#endif - - static void ConfigureSecureContext(SecureContext* sc); - static void AddMethods(Environment* env, v8::Local t); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - static SSL_SESSION* GetSessionCallback(SSL* s, - unsigned char* key, - int len, - int* copy); -#else - static SSL_SESSION* GetSessionCallback(SSL* s, - const unsigned char* key, - int len, - int* copy); -#endif - static int NewSessionCallback(SSL* s, SSL_SESSION* sess); - static void OnClientHello(void* arg, - const ClientHelloParser::ClientHello& hello); - - static void GetPeerCertificate( - const v8::FunctionCallbackInfo& args); - static void GetFinished(const v8::FunctionCallbackInfo& args); - static void GetPeerFinished(const v8::FunctionCallbackInfo& args); - static void GetSession(const v8::FunctionCallbackInfo& args); - static void SetSession(const v8::FunctionCallbackInfo& args); - static void LoadSession(const v8::FunctionCallbackInfo& args); - static void IsSessionReused(const v8::FunctionCallbackInfo& args); - static void IsInitFinished(const v8::FunctionCallbackInfo& args); - static void VerifyError(const v8::FunctionCallbackInfo& args); - static void GetCurrentCipher(const v8::FunctionCallbackInfo& args); - static void EndParser(const v8::FunctionCallbackInfo& args); - static void CertCbDone(const v8::FunctionCallbackInfo& args); - static void Renegotiate(const v8::FunctionCallbackInfo& args); - static void Shutdown(const v8::FunctionCallbackInfo& args); - static void GetTLSTicket(const v8::FunctionCallbackInfo& args); - static void NewSessionDone(const v8::FunctionCallbackInfo& args); - static void SetOCSPResponse(const v8::FunctionCallbackInfo& args); - static void RequestOCSP(const v8::FunctionCallbackInfo& args); - static void GetEphemeralKeyInfo( - const v8::FunctionCallbackInfo& args); - static void GetProtocol(const v8::FunctionCallbackInfo& args); - -#ifdef SSL_set_max_send_fragment - static void SetMaxSendFragment( - const v8::FunctionCallbackInfo& args); -#endif // SSL_set_max_send_fragment - - static void GetALPNNegotiatedProto( - const v8::FunctionCallbackInfo& args); - static void SetALPNProtocols(const v8::FunctionCallbackInfo& args); - static int SelectALPNCallback(SSL* s, - const unsigned char** out, - unsigned char* outlen, - const unsigned char* in, - unsigned int inlen, - void* arg); - static int TLSExtStatusCallback(SSL* s, void* arg); - static int SSLCertCallback(SSL* s, void* arg); - - void DestroySSL(); - void WaitForCertCb(CertCb cb, void* arg); - void SetSNIContext(SecureContext* sc); - int SetCACerts(SecureContext* sc); - - inline Environment* ssl_env() const { - return env_; - } - - Environment* const env_; - Kind kind_; - SSLSessionPointer next_sess_; - SSLPointer ssl_; - bool session_callbacks_; - bool new_session_wait_; - - // SSL_set_cert_cb - CertCb cert_cb_; - void* cert_cb_arg_; - bool cert_cb_running_; - - ClientHelloParser hello_parser_; - - Persistent ocsp_response_; - Persistent sni_context_; - - friend class SecureContext; -}; - class CipherBase : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index eb40d856fdab4b..1ac48a2e489fb5 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -30,11 +30,24 @@ #include "node_internals.h" #include "stream_base-inl.h" #include "util-inl.h" +#include "node_errors.h" namespace node { +using crypto::BignumPointer; +using crypto::BIOPointer; +using crypto::ClearErrorOnReturn; +using crypto::ClientHelloParser; +using crypto::EVPKeyPointer; +using crypto::OpenSSLBuffer; +using crypto::RSAPointer; using crypto::SecureContext; -using crypto::SSLWrap; +using crypto::SSLPointer; +using crypto::StackOfASN1; +using crypto::StackOfX509; +using crypto::X509Pointer; +using v8::Array; +using v8::Boolean; using v8::Context; using v8::DontDelete; using v8::EscapableHandleScope; @@ -42,13 +55,376 @@ using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; +using v8::Integer; +using v8::Isolate; using v8::Local; +using v8::NewStringType; using v8::Object; using v8::ReadOnly; using v8::Signature; using v8::String; using v8::Value; + +static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL + | ASN1_STRFLGS_UTF8_CONVERT + | XN_FLAG_SEP_MULTILINE + | XN_FLAG_FN_SN; + +static void AddFingerprintDigest(const unsigned char* md, + unsigned int md_size, + char (*fingerprint)[3 * EVP_MAX_MD_SIZE + 1]) { + unsigned int i; + const char hex[] = "0123456789ABCDEF"; + + for (i = 0; i < md_size; i++) { + (*fingerprint)[3*i] = hex[(md[i] & 0xf0) >> 4]; + (*fingerprint)[(3*i)+1] = hex[(md[i] & 0x0f)]; + (*fingerprint)[(3*i)+2] = ':'; + } + + if (md_size > 0) { + (*fingerprint)[(3*(md_size-1))+2] = '\0'; + } else { + (*fingerprint)[0] = '\0'; + } +} + +static bool SafeX509ExtPrint(BIO* out, X509_EXTENSION* ext) { + const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); + + if (method != X509V3_EXT_get_nid(NID_subject_alt_name)) + return false; + + GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); + if (names == nullptr) + return false; + + for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); + + if (i != 0) + BIO_write(out, ", ", 2); + + if (gen->type == GEN_DNS) { + ASN1_IA5STRING* name = gen->d.dNSName; + + BIO_write(out, "DNS:", 4); + BIO_write(out, name->data, name->length); + } else { + STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( + const_cast(method), gen, nullptr); + if (nval == nullptr) + return false; + X509V3_EXT_val_prn(out, nval, 0, 0); + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + + return true; +} + +static Local X509ToObject(Environment* env, X509* cert) { + EscapableHandleScope scope(env->isolate()); + Local context = env->context(); + Local info = Object::New(env->isolate()); + + BIOPointer bio(BIO_new(BIO_s_mem())); + BUF_MEM* mem; + if (X509_NAME_print_ex(bio.get(), + X509_get_subject_name(cert), + 0, + X509_NAME_FLAGS) > 0) { + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->subject_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + } + USE(BIO_reset(bio.get())); + + X509_NAME* issuer_name = X509_get_issuer_name(cert); + if (X509_NAME_print_ex(bio.get(), issuer_name, 0, X509_NAME_FLAGS) > 0) { + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->issuer_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + } + USE(BIO_reset(bio.get())); + + int nids[] = { NID_subject_alt_name, NID_info_access }; + Local keys[] = { env->subjectaltname_string(), + env->infoaccess_string() }; + CHECK_EQ(arraysize(nids), arraysize(keys)); + for (size_t i = 0; i < arraysize(nids); i++) { + int index = X509_get_ext_by_NID(cert, nids[i], -1); + if (index < 0) + continue; + + X509_EXTENSION* ext; + int rv; + + ext = X509_get_ext(cert, index); + CHECK_NOT_NULL(ext); + + if (!SafeX509ExtPrint(bio.get(), ext)) { + rv = X509V3_EXT_print(bio.get(), ext, 0, 0); + CHECK_EQ(rv, 1); + } + + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, keys[i], + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + + USE(BIO_reset(bio.get())); + } + + EVPKeyPointer pkey(X509_get_pubkey(cert)); + RSAPointer rsa; + if (pkey) + rsa.reset(EVP_PKEY_get1_RSA(pkey.get())); + + if (rsa) { + const BIGNUM* n; + const BIGNUM* e; + RSA_get0_key(rsa.get(), &n, &e, nullptr); + BN_print(bio.get(), n); + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->modulus_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + USE(BIO_reset(bio.get())); + + uint64_t exponent_word = static_cast(BN_get_word(e)); + uint32_t lo = static_cast(exponent_word); + uint32_t hi = static_cast(exponent_word >> 32); + if (hi == 0) { + BIO_printf(bio.get(), "0x%x", lo); + } else { + BIO_printf(bio.get(), "0x%x%08x", hi, lo); + } + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->exponent_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + USE(BIO_reset(bio.get())); + + int size = i2d_RSA_PUBKEY(rsa.get(), nullptr); + CHECK_GE(size, 0); + Local pubbuff = Buffer::New(env, size).ToLocalChecked(); + unsigned char* pubserialized = + reinterpret_cast(Buffer::Data(pubbuff)); + i2d_RSA_PUBKEY(rsa.get(), &pubserialized); + info->Set(env->pubkey_string(), pubbuff); + } + + pkey.reset(); + rsa.reset(); + + ASN1_TIME_print(bio.get(), X509_get_notBefore(cert)); + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->valid_from_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + USE(BIO_reset(bio.get())); + + ASN1_TIME_print(bio.get(), X509_get_notAfter(cert)); + BIO_get_mem_ptr(bio.get(), &mem); + info->Set(context, env->valid_to_string(), + String::NewFromUtf8(env->isolate(), mem->data, + NewStringType::kNormal, + mem->length).ToLocalChecked()).FromJust(); + bio.reset(); + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int md_size; + char fingerprint[EVP_MAX_MD_SIZE * 3 + 1]; + if (X509_digest(cert, EVP_sha1(), md, &md_size)) { + AddFingerprintDigest(md, md_size, &fingerprint); + info->Set(context, env->fingerprint_string(), + OneByteString(env->isolate(), fingerprint)).FromJust(); + } + if (X509_digest(cert, EVP_sha256(), md, &md_size)) { + AddFingerprintDigest(md, md_size, &fingerprint); + info->Set(context, env->fingerprint256_string(), + OneByteString(env->isolate(), fingerprint)).FromJust(); + } + + StackOfASN1 eku(static_cast( + X509_get_ext_d2i(cert, NID_ext_key_usage, nullptr, nullptr))); + if (eku) { + Local ext_key_usage = Array::New(env->isolate()); + char buf[256]; + + int j = 0; + for (int i = 0; i < sk_ASN1_OBJECT_num(eku.get()); i++) { + if (OBJ_obj2txt(buf, + sizeof(buf), + sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { + ext_key_usage->Set(context, + j++, + OneByteString(env->isolate(), buf)).FromJust(); + } + } + + eku.reset(); + info->Set(context, env->ext_key_usage_string(), ext_key_usage).FromJust(); + } + + if (ASN1_INTEGER* serial_number = X509_get_serialNumber(cert)) { + BignumPointer bn(ASN1_INTEGER_to_BN(serial_number, nullptr)); + if (bn) { + OpenSSLBuffer buf(BN_bn2hex(bn.get())); + if (buf) { + info->Set(context, env->serial_number_string(), + OneByteString(env->isolate(), buf.get())).FromJust(); + } + } + } + + // Raw DER certificate + int size = i2d_X509(cert, nullptr); + Local buff = Buffer::New(env, size).ToLocalChecked(); + unsigned char* serialized = reinterpret_cast( + Buffer::Data(buff)); + i2d_X509(cert, &serialized); + info->Set(context, env->raw_string(), buff).FromJust(); + + return scope.Escape(info); +} + + +int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) { + X509_STORE* store = SSL_CTX_get_cert_store(ctx); + DeleteFnPtr store_ctx( + X509_STORE_CTX_new()); + return store_ctx.get() != nullptr && + X509_STORE_CTX_init(store_ctx.get(), store, nullptr, nullptr) == 1 && + X509_STORE_CTX_get1_issuer(issuer, store_ctx.get(), cert) == 1; +} + + +static Local AddIssuerChainToObject(X509Pointer* cert, + Local object, + StackOfX509&& peer_certs, + Environment* const env) { + Local context = env->isolate()->GetCurrentContext(); + cert->reset(sk_X509_delete(peer_certs.get(), 0)); + for (;;) { + int i; + for (i = 0; i < sk_X509_num(peer_certs.get()); i++) { + X509* ca = sk_X509_value(peer_certs.get(), i); + if (X509_check_issued(ca, cert->get()) != X509_V_OK) + continue; + + Local ca_info = X509ToObject(env, ca); + object->Set(context, env->issuercert_string(), ca_info).FromJust(); + object = ca_info; + + // NOTE: Intentionally freeing cert that is not used anymore. + // Delete cert and continue aggregating issuers. + cert->reset(sk_X509_delete(peer_certs.get(), i)); + break; + } + + // Issuer not found, break out of the loop. + if (i == sk_X509_num(peer_certs.get())) + break; + } + return object; +} + + +static StackOfX509 CloneSSLCerts(X509Pointer&& cert, + const STACK_OF(X509)* const ssl_certs) { + StackOfX509 peer_certs(sk_X509_new(nullptr)); + if (cert) + sk_X509_push(peer_certs.get(), cert.release()); + for (int i = 0; i < sk_X509_num(ssl_certs); i++) { + X509Pointer cert(X509_dup(sk_X509_value(ssl_certs, i))); + if (!cert || !sk_X509_push(peer_certs.get(), cert.get())) + return StackOfX509(); + // `cert` is now managed by the stack. + cert.release(); + } + return peer_certs; +} + + +static Local GetLastIssuedCert(X509Pointer* cert, + const SSLPointer& ssl, + Local issuer_chain, + Environment* const env) { + Local context = env->isolate()->GetCurrentContext(); + while (X509_check_issued(cert->get(), cert->get()) != X509_V_OK) { + X509* ca; + if (SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl.get()), cert->get(), &ca) <= 0) + break; + + Local ca_info = X509ToObject(env, ca); + issuer_chain->Set(context, env->issuercert_string(), ca_info).FromJust(); + issuer_chain = ca_info; + + // Delete previous cert and continue aggregating issuers. + cert->reset(ca); + } + return issuer_chain; +} + + +int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { + // Quoting SSL_set_verify(3ssl): + // + // 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, + // whether the verification of the certificate in question was passed + // (preverify_ok=1) or not (preverify_ok=0). x509_ctx is a pointer to + // 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 + // certificate. At each level signatures and issuer attributes are + // checked. Whenever a verification error is found, the error number is + // stored in x509_ctx and VerifyCallback is called with preverify_ok=0. + // By applying X509_CTX_store_* functions VerifyCallback can locate the + // certificate in question and perform additional steps (see EXAMPLES). + // If no error is found for a certificate, VerifyCallback is called + // 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 + // verification process is immediately stopped with "verification + // failed" state. If SSL_VERIFY_PEER is set, a verification failure + // alert is sent to the peer and the TLS/SSL handshake is terminated. If + // VerifyCallback returns 1, the verification process is continued. If + // VerifyCallback always returns 1, the TLS/SSL handshake will not be + // terminated with respect to verification failures and the connection + // will be established. The calling process can however retrieve the + // error code of the last verification error using + // SSL_get_verify_result(3) or by maintaining its own error storage + // managed by VerifyCallback. + // + // If no VerifyCallback is specified, the default callback will be + // used. Its return value is identical to preverify_ok, so that any + // verification failure will lead to a termination of the TLS/SSL + // handshake with an alert message, if SSL_VERIFY_PEER is set. + // + // Since we cannot perform I/O quickly enough in this callback, we ignore + // all preverify_ok errors and let the handshake continue. It is + // imparative that the user use Connection::VerifyError after the + // 'secure' callback has been made. + return 1; +} + + TLSWrap::TLSWrap(Environment* env, Kind kind, StreamBase* stream, @@ -57,7 +433,6 @@ TLSWrap::TLSWrap(Environment* env, env->tls_wrap_constructor_function() ->NewInstance(env->context()).ToLocalChecked(), AsyncWrap::PROVIDER_TLSWRAP), - SSLWrap(env, sc, kind), StreamBase(env), sc_(sc), write_size_(0), @@ -65,17 +440,27 @@ TLSWrap::TLSWrap(Environment* env, established_(false), shutdown_(false), cycle_depth_(0), + env_(env), + kind_(kind), + next_sess_(nullptr), + session_callbacks_(false), + new_session_wait_(false), + cert_cb_(nullptr), + cert_cb_arg_(nullptr), + cert_cb_running_(false), eof_(false) { + ssl_.reset(SSL_new(sc->ctx_.get())); + CHECK(ssl_); + env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); + MakeWeak(); // sc comes from an Unwrap. Make sure it was assigned. CHECK_NOT_NULL(sc); // We've our own session callbacks - SSL_CTX_sess_set_get_cb(sc_->ctx_.get(), - SSLWrap::GetSessionCallback); - SSL_CTX_sess_set_new_cb(sc_->ctx_.get(), - SSLWrap::NewSessionCallback); + SSL_CTX_sess_set_get_cb(sc_->ctx_.get(), GetSessionCallback); + SSL_CTX_sess_set_new_cb(sc_->ctx_.get(), NewSessionCallback); stream->PushStreamListener(this); @@ -84,9 +469,18 @@ TLSWrap::TLSWrap(Environment* env, TLSWrap::~TLSWrap() { + DestroySSL(); sc_ = nullptr; } +void TLSWrap::DestroySSL() { + if (!ssl_) + return; + + env_->isolate()->AdjustAmountOfExternalAllocatedMemory(-kExternalSize); + ssl_.reset(); +} + bool TLSWrap::InvokeQueued(int status, const char* error_str) { if (!write_callback_scheduled_) @@ -115,7 +509,7 @@ void TLSWrap::InitSSL() { SSL_set_bio(ssl_.get(), enc_in_, enc_out_); // NOTE: This could be overridden in SetVerifyMode - SSL_set_verify(ssl_.get(), SSL_VERIFY_NONE, crypto::VerifyCallback); + SSL_set_verify(ssl_.get(), SSL_VERIFY_NONE, VerifyCallback); #ifdef SSL_MODE_RELEASE_BUFFERS long mode = SSL_get_mode(ssl_.get()); // NOLINT(runtime/int) @@ -132,7 +526,7 @@ void TLSWrap::InitSSL() { ConfigureSecureContext(sc_); - SSL_set_cert_cb(ssl_.get(), SSLWrap::SSLCertCallback, this); + SSL_set_cert_cb(ssl_.get(), SSLCertCallback, this); if (is_server()) { SSL_set_accept_state(ssl_.get()); @@ -157,8 +551,7 @@ void TLSWrap::Wrap(const FunctionCallbackInfo& args) { Local stream_obj = args[0].As(); Local sc = args[1].As(); - Kind kind = args[2]->IsTrue() ? SSLWrap::kServer : - SSLWrap::kClient; + Kind kind = args[2]->IsTrue() ? kServer : kClient; StreamBase* stream = static_cast(stream_obj->Value()); CHECK_NOT_NULL(stream); @@ -169,6 +562,751 @@ void TLSWrap::Wrap(const FunctionCallbackInfo& args) { } +void TLSWrap::ConfigureSecureContext(SecureContext* sc) { + // OCSP stapling + SSL_CTX_set_tlsext_status_cb(sc->ctx_.get(), TLSExtStatusCallback); + SSL_CTX_set_tlsext_status_arg(sc->ctx_.get(), nullptr); +} + + +SSL_SESSION* TLSWrap::GetSessionCallback(SSL* s, + const unsigned char* key, + int len, + int* copy) { + TLSWrap* w = static_cast(SSL_get_app_data(s)); + + *copy = 0; + return w->next_sess_.release(); +} + + +int TLSWrap::NewSessionCallback(SSL* s, SSL_SESSION* sess) { + TLSWrap* w = static_cast(SSL_get_app_data(s)); + Environment* env = w->ssl_env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + if (!w->session_callbacks_) + return 0; + + // Check if session is small enough to be stored + int size = i2d_SSL_SESSION(sess, nullptr); + if (size > SecureContext::kMaxSessionSize) + return 0; + + // Serialize session + Local buff = Buffer::New(env, size).ToLocalChecked(); + unsigned char* serialized = reinterpret_cast( + Buffer::Data(buff)); + memset(serialized, 0, size); + i2d_SSL_SESSION(sess, &serialized); + + unsigned int session_id_length; + const unsigned char* session_id = SSL_SESSION_get_id(sess, + &session_id_length); + Local session = Buffer::Copy( + env, + reinterpret_cast(session_id), + session_id_length).ToLocalChecked(); + Local argv[] = { session, buff }; + w->new_session_wait_ = true; + w->MakeCallback(env->onnewsession_string(), arraysize(argv), argv); + + return 0; +} + + +void TLSWrap::OnClientHello(void* arg, + const ClientHelloParser::ClientHello& hello) { + TLSWrap* w = static_cast(arg); + Environment* env = w->ssl_env(); + HandleScope handle_scope(env->isolate()); + Local context = env->context(); + Context::Scope context_scope(context); + + Local hello_obj = Object::New(env->isolate()); + Local buff = Buffer::Copy( + env, + reinterpret_cast(hello.session_id()), + hello.session_size()).ToLocalChecked(); + hello_obj->Set(context, env->session_id_string(), buff).FromJust(); + if (hello.servername() == nullptr) { + hello_obj->Set(context, + env->servername_string(), + String::Empty(env->isolate())).FromJust(); + } else { + Local servername = OneByteString(env->isolate(), + hello.servername(), + hello.servername_size()); + hello_obj->Set(context, env->servername_string(), servername).FromJust(); + } + hello_obj->Set(context, + env->tls_ticket_string(), + Boolean::New(env->isolate(), hello.has_ticket())).FromJust(); + hello_obj->Set(context, + env->ocsp_request_string(), + Boolean::New(env->isolate(), hello.ocsp_request())).FromJust(); + + Local argv[] = { hello_obj }; + w->MakeCallback(env->onclienthello_string(), arraysize(argv), argv); +} + + +void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->ssl_env(); + + ClearErrorOnReturn clear_error_on_return; + + Local result; + // Used to build the issuer certificate chain. + Local issuer_chain; + + // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` + // contains the `peer_certificate`, but on server it doesn't. + X509Pointer cert( + w->is_server() ? SSL_get_peer_certificate(w->ssl_.get()) : nullptr); + STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(w->ssl_.get()); + if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) + goto done; + + // Short result requested. + if (args.Length() < 1 || !args[0]->IsTrue()) { + result = X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); + goto done; + } + + if (auto peer_certs = CloneSSLCerts(std::move(cert), ssl_certs)) { + // First and main certificate. + X509Pointer cert(sk_X509_value(peer_certs.get(), 0)); + CHECK(cert); + result = X509ToObject(env, cert.release()); + + issuer_chain = + AddIssuerChainToObject(&cert, result, std::move(peer_certs), env); + issuer_chain = GetLastIssuedCert(&cert, w->ssl_, issuer_chain, env); + // Last certificate should be self-signed. + if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) + issuer_chain->Set(env->context(), + env->issuercert_string(), + issuer_chain).FromJust(); + } + + done: + if (result.IsEmpty()) + result = Object::New(env->isolate()); + args.GetReturnValue().Set(result); +} + + +void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + // We cannot just pass nullptr to SSL_get_finished() + // because it would further be propagated to memcpy(), + // where the standard requirements as described in ISO/IEC 9899:2011 + // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. + // Thus, we use a dummy byte. + char dummy[1]; + size_t len = SSL_get_finished(w->ssl_.get(), dummy, sizeof dummy); + if (len == 0) + return; + + char* buf = Malloc(len); + CHECK_EQ(len, SSL_get_finished(w->ssl_.get(), buf, len)); + args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked()); +} + + +void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + // We cannot just pass nullptr to SSL_get_peer_finished() + // because it would further be propagated to memcpy(), + // where the standard requirements as described in ISO/IEC 9899:2011 + // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. + // Thus, we use a dummy byte. + char dummy[1]; + size_t len = SSL_get_peer_finished(w->ssl_.get(), dummy, sizeof dummy); + if (len == 0) + return; + + char* buf = Malloc(len); + CHECK_EQ(len, SSL_get_peer_finished(w->ssl_.get(), buf, len)); + args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked()); +} + + +void TLSWrap::GetSession(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + SSL_SESSION* sess = SSL_get_session(w->ssl_.get()); + if (sess == nullptr) + return; + + int slen = i2d_SSL_SESSION(sess, nullptr); + CHECK_GT(slen, 0); + + char* sbuf = Malloc(slen); + unsigned char* p = reinterpret_cast(sbuf); + i2d_SSL_SESSION(sess, &p); + args.GetReturnValue().Set(Buffer::New(env, sbuf, slen).ToLocalChecked()); +} + + +void TLSWrap::SetSession(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + if (args.Length() < 1) { + return THROW_ERR_MISSING_ARGS(env, "Session argument is mandatory"); + } + + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session"); + size_t slen = Buffer::Length(args[0]); + std::vector sbuf(slen); + if (char* p = Buffer::Data(args[0])) + sbuf.assign(p, p + slen); + + const unsigned char* p = reinterpret_cast(sbuf.data()); + SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, slen)); + + if (sess == nullptr) + return; + + int r = SSL_set_session(w->ssl_.get(), sess.get()); + + if (!r) + return env->ThrowError("SSL_set_session error"); +} + + +void TLSWrap::LoadSession(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + if (args.Length() >= 1 && Buffer::HasInstance(args[0])) { + ssize_t slen = Buffer::Length(args[0]); + char* sbuf = Buffer::Data(args[0]); + + const unsigned char* p = reinterpret_cast(sbuf); + SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, slen); + + // Setup next session and move hello to the BIO buffer + w->next_sess_.reset(sess); + } +} + + +void TLSWrap::IsSessionReused(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + bool yes = SSL_session_reused(w->ssl_.get()); + args.GetReturnValue().Set(yes); +} + + +void TLSWrap::EndParser(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + w->hello_parser_.End(); +} + + +void TLSWrap::Renegotiate(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + ClearErrorOnReturn clear_error_on_return; + + bool yes = SSL_renegotiate(w->ssl_.get()) == 1; + args.GetReturnValue().Set(yes); +} + + +void TLSWrap::GetTLSTicket(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->ssl_env(); + + SSL_SESSION* sess = SSL_get_session(w->ssl_.get()); + if (sess == nullptr) + return; + + const unsigned char* ticket; + size_t length; + SSL_SESSION_get0_ticket(sess, &ticket, &length); + + if (ticket == nullptr) + return; + + Local buff = Buffer::Copy( + env, reinterpret_cast(ticket), length).ToLocalChecked(); + + args.GetReturnValue().Set(buff); +} + + +void TLSWrap::NewSessionDone(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + w->new_session_wait_ = false; + w->NewSessionDoneCb(); +} + + +void TLSWrap::SetOCSPResponse(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->env(); + + if (args.Length() < 1) + return THROW_ERR_MISSING_ARGS(env, "OCSP response argument is mandatory"); + + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response"); + + w->ocsp_response_.Reset(args.GetIsolate(), args[0].As()); +} + + +void TLSWrap::RequestOCSP(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + SSL_set_tlsext_status_type(w->ssl_.get(), TLSEXT_STATUSTYPE_ocsp); +} + + +void TLSWrap::GetEphemeralKeyInfo(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + + CHECK(w->ssl_); + + // tmp key is available on only client + if (w->is_server()) + return args.GetReturnValue().SetNull(); + + Local info = Object::New(env->isolate()); + + EVP_PKEY* key; + + if (SSL_get_server_tmp_key(w->ssl_.get(), &key)) { + int kid = EVP_PKEY_id(key); + switch (kid) { + case EVP_PKEY_DH: + info->Set(context, env->type_string(), + FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).FromJust(); + info->Set(context, env->size_string(), + Integer::New(env->isolate(), EVP_PKEY_bits(key))).FromJust(); + break; + case EVP_PKEY_EC: + // TODO(shigeki) Change this to EVP_PKEY_X25519 and add EVP_PKEY_X448 + // after upgrading to 1.1.1. + case NID_X25519: + { + const char* curve_name; + if (kid == EVP_PKEY_EC) { + EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + curve_name = OBJ_nid2sn(nid); + EC_KEY_free(ec); + } else { + curve_name = OBJ_nid2sn(kid); + } + info->Set(context, env->type_string(), + FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH")).FromJust(); + info->Set(context, env->name_string(), + OneByteString(args.GetIsolate(), curve_name)).FromJust(); + info->Set(context, env->size_string(), + Integer::New(env->isolate(), + EVP_PKEY_bits(key))).FromJust(); + } + break; + } + EVP_PKEY_free(key); + } + + return args.GetReturnValue().Set(info); +} + + +#ifdef SSL_set_max_send_fragment +void TLSWrap::SetMaxSendFragment(const FunctionCallbackInfo& args) { + CHECK(args.Length() >= 1 && args[0]->IsNumber()); + + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + int rv = SSL_set_max_send_fragment( + w->ssl_.get(), + args[0]->Int32Value(w->ssl_env()->context()).FromJust()); + args.GetReturnValue().Set(rv); +} +#endif // SSL_set_max_send_fragment + + +void TLSWrap::VerifyError(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + // XXX(bnoordhuis) The UNABLE_TO_GET_ISSUER_CERT error when there is no + // peer certificate is questionable but it's compatible with what was + // here before. + long x509_verify_error = // NOLINT(runtime/int) + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + if (X509* peer_cert = SSL_get_peer_certificate(w->ssl_.get())) { + X509_free(peer_cert); + x509_verify_error = SSL_get_verify_result(w->ssl_.get()); + } + + if (x509_verify_error == X509_V_OK) + return args.GetReturnValue().SetNull(); + + const char* reason = X509_verify_cert_error_string(x509_verify_error); + const char* code = reason; +#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; + switch (x509_verify_error) { + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) + CASE_X509_ERR(UNABLE_TO_GET_CRL) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) + CASE_X509_ERR(CERT_SIGNATURE_FAILURE) + CASE_X509_ERR(CRL_SIGNATURE_FAILURE) + CASE_X509_ERR(CERT_NOT_YET_VALID) + CASE_X509_ERR(CERT_HAS_EXPIRED) + CASE_X509_ERR(CRL_NOT_YET_VALID) + CASE_X509_ERR(CRL_HAS_EXPIRED) + CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) + CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) + CASE_X509_ERR(OUT_OF_MEM) + CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) + CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) + CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) + CASE_X509_ERR(CERT_CHAIN_TOO_LONG) + CASE_X509_ERR(CERT_REVOKED) + CASE_X509_ERR(INVALID_CA) + CASE_X509_ERR(PATH_LENGTH_EXCEEDED) + CASE_X509_ERR(INVALID_PURPOSE) + CASE_X509_ERR(CERT_UNTRUSTED) + CASE_X509_ERR(CERT_REJECTED) + } +#undef CASE_X509_ERR + + Isolate* isolate = args.GetIsolate(); + Local reason_string = OneByteString(isolate, reason); + Local exception_value = Exception::Error(reason_string); + Local exception_object = exception_value->ToObject(isolate); + exception_object->Set(w->env()->context(), w->env()->code_string(), + OneByteString(isolate, code)).FromJust(); + args.GetReturnValue().Set(exception_object); +} + + +void TLSWrap::GetCurrentCipher(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->ssl_env(); + Local context = env->context(); + + const SSL_CIPHER* c = SSL_get_current_cipher(w->ssl_.get()); + if (c == nullptr) + return; + + Local info = Object::New(env->isolate()); + const char* cipher_name = SSL_CIPHER_get_name(c); + info->Set(context, env->name_string(), + OneByteString(args.GetIsolate(), cipher_name)).FromJust(); + info->Set(context, env->version_string(), + OneByteString(args.GetIsolate(), "TLSv1/SSLv3")).FromJust(); + args.GetReturnValue().Set(info); +} + + +void TLSWrap::GetProtocol(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + const char* tls_version = SSL_get_version(w->ssl_.get()); + args.GetReturnValue().Set(OneByteString(args.GetIsolate(), tls_version)); +} + + +int TLSWrap::SelectALPNCallback(SSL* s, + const unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* arg) { + TLSWrap* w = static_cast(SSL_get_app_data(s)); + Environment* env = w->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local alpn_buffer = + w->object()->GetPrivate( + env->context(), + env->alpn_buffer_private_symbol()).ToLocalChecked(); + CHECK(Buffer::HasInstance(alpn_buffer)); + const unsigned char* alpn_protos = + reinterpret_cast(Buffer::Data(alpn_buffer)); + unsigned alpn_protos_len = Buffer::Length(alpn_buffer); + int status = SSL_select_next_proto(const_cast(out), outlen, + alpn_protos, alpn_protos_len, in, inlen); + // According to 3.2. Protocol Selection of RFC7301, fatal + // no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not + // support it yet. See + // https://rt.openssl.org/Ticket/Display.html?id=3463&user=guest&pass=guest + return status == OPENSSL_NPN_NEGOTIATED ? SSL_TLSEXT_ERR_OK + : SSL_TLSEXT_ERR_NOACK; +} + + +void TLSWrap::GetALPNNegotiatedProto(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + + const unsigned char* alpn_proto; + unsigned int alpn_proto_len; + + SSL_get0_alpn_selected(w->ssl_.get(), &alpn_proto, &alpn_proto_len); + + if (!alpn_proto) + return args.GetReturnValue().Set(false); + + args.GetReturnValue().Set( + OneByteString(args.GetIsolate(), alpn_proto, alpn_proto_len)); +} + + +void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->env(); + if (args.Length() < 1 || !Buffer::HasInstance(args[0])) + return env->ThrowTypeError("Must give a Buffer as first argument"); + + if (w->is_client()) { + const unsigned char* alpn_protos = + reinterpret_cast(Buffer::Data(args[0])); + unsigned alpn_protos_len = Buffer::Length(args[0]); + int r = SSL_set_alpn_protos(w->ssl_.get(), alpn_protos, alpn_protos_len); + CHECK_EQ(r, 0); + } else { + CHECK( + w->object()->SetPrivate( + env->context(), + env->alpn_buffer_private_symbol(), + args[0]).FromJust()); + // Server should select ALPN protocol from list of advertised by client + SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(w->ssl_.get()), + SelectALPNCallback, + nullptr); + } +} + + +int TLSWrap::TLSExtStatusCallback(SSL* s, void* arg) { + TLSWrap* w = static_cast(SSL_get_app_data(s)); + Environment* env = w->env(); + HandleScope handle_scope(env->isolate()); + + if (w->is_client()) { + // Incoming response + const unsigned char* resp; + int len = SSL_get_tlsext_status_ocsp_resp(s, &resp); + Local arg; + if (resp == nullptr) { + arg = Null(env->isolate()); + } else { + arg = + Buffer::Copy(env, reinterpret_cast(resp), len) + .ToLocalChecked(); + } + + w->MakeCallback(env->onocspresponse_string(), 1, &arg); + + // Somehow, client is expecting different return value here + return 1; + } else { + // Outgoing response + if (w->ocsp_response_.IsEmpty()) + return SSL_TLSEXT_ERR_NOACK; + + Local obj = PersistentToLocal(env->isolate(), w->ocsp_response_); + char* resp = Buffer::Data(obj); + size_t len = Buffer::Length(obj); + + // OpenSSL takes control of the pointer after accepting it + char* data = node::Malloc(len); + memcpy(data, resp, len); + + if (!SSL_set_tlsext_status_ocsp_resp(s, data, len)) + free(data); + w->ocsp_response_.Reset(); + + return SSL_TLSEXT_ERR_OK; + } +} + + +void TLSWrap::WaitForCertCb(CertCb cb, void* arg) { + cert_cb_ = cb; + cert_cb_arg_ = arg; +} + + +int TLSWrap::SSLCertCallback(SSL* s, void* arg) { + TLSWrap* w = static_cast(SSL_get_app_data(s)); + + if (!w->is_server()) + return 1; + + if (!w->is_waiting_cert_cb()) + return 1; + + if (w->cert_cb_running_) + return -1; + + Environment* env = w->env(); + Local context = env->context(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(context); + w->cert_cb_running_ = true; + + Local info = Object::New(env->isolate()); + + const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + if (servername == nullptr) { + info->Set(context, + env->servername_string(), + String::Empty(env->isolate())).FromJust(); + } else { + Local str = OneByteString(env->isolate(), servername, + strlen(servername)); + info->Set(context, env->servername_string(), str).FromJust(); + } + + const bool ocsp = (SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); + info->Set(context, env->ocsp_request_string(), + Boolean::New(env->isolate(), ocsp)).FromJust(); + + Local argv[] = { info }; + w->MakeCallback(env->oncertcb_string(), arraysize(argv), argv); + + if (!w->cert_cb_running_) + return 1; + + // Performing async action, wait... + return -1; +} + + +void TLSWrap::CertCbDone(const FunctionCallbackInfo& args) { + TLSWrap* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); + Environment* env = w->env(); + + CHECK(w->is_waiting_cert_cb() && w->cert_cb_running_); + + Local object = w->object(); + Local ctx = object->Get(env->sni_context_string()); + Local cons = env->secure_context_constructor_template(); + + // Not an object, probably undefined or null + if (!ctx->IsObject()) + goto fire_cb; + + if (cons->HasInstance(ctx)) { + SecureContext* sc; + ASSIGN_OR_RETURN_UNWRAP(&sc, ctx.As()); + w->sni_context_.Reset(env->isolate(), ctx); + + int rv; + + // NOTE: reference count is not increased by this API methods + X509* x509 = SSL_CTX_get0_certificate(sc->ctx_.get()); + EVP_PKEY* pkey = SSL_CTX_get0_privatekey(sc->ctx_.get()); + STACK_OF(X509)* chain; + + rv = SSL_CTX_get0_chain_certs(sc->ctx_.get(), &chain); + if (rv) + rv = SSL_use_certificate(w->ssl_.get(), x509); + if (rv) + rv = SSL_use_PrivateKey(w->ssl_.get(), pkey); + if (rv && chain != nullptr) + rv = SSL_set1_chain(w->ssl_.get(), chain); + if (rv) + rv = w->SetCACerts(sc); + if (!rv) { + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) + if (!err) + return env->ThrowError("CertCbDone"); + return crypto::ThrowCryptoError(env, err); + } + } else { + // Failure: incorrect SNI context object + Local err = Exception::TypeError(env->sni_context_err_string()); + w->MakeCallback(env->onerror_string(), 1, &err); + return; + } + + fire_cb: + CertCb cb; + void* arg; + + cb = w->cert_cb_; + arg = w->cert_cb_arg_; + + w->cert_cb_running_ = false; + w->cert_cb_ = nullptr; + w->cert_cb_arg_ = nullptr; + + cb(arg); +} + + +void TLSWrap::SetSNIContext(SecureContext* sc) { + ConfigureSecureContext(sc); + CHECK_EQ(SSL_set_SSL_CTX(ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); + + SetCACerts(sc); +} + + +int TLSWrap::SetCACerts(SecureContext* sc) { + int err = SSL_set1_verify_cert_store(ssl_.get(), + SSL_CTX_get_cert_store(sc->ctx_.get())); + if (err != 1) + return err; + + STACK_OF(X509_NAME)* list = SSL_dup_CA_list( + SSL_CTX_get_client_CA_list(sc->ctx_.get())); + + // NOTE: `SSL_set_client_CA_list` takes the ownership of `list` + SSL_set_client_CA_list(ssl_.get(), list); + return 1; +} + + void TLSWrap::Receive(const FunctionCallbackInfo& args) { TLSWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); @@ -723,7 +1861,7 @@ void TLSWrap::SetVerifyMode(const FunctionCallbackInfo& args) { } // Always allow a connection. We'll reject in javascript. - SSL_set_verify(wrap->ssl_.get(), verify_mode, crypto::VerifyCallback); + SSL_set_verify(wrap->ssl_.get(), verify_mode, VerifyCallback); } @@ -734,9 +1872,7 @@ void TLSWrap::EnableSessionCallbacks( CHECK_NOT_NULL(wrap->ssl_); wrap->enable_session_callbacks(); crypto::NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength); - wrap->hello_parser_.Start(SSLWrap::OnClientHello, - OnClientHelloParseEnd, - wrap); + wrap->hello_parser_.Start(OnClientHello, OnClientHelloParseEnd, wrap); } @@ -751,7 +1887,7 @@ void TLSWrap::DestroySSL(const FunctionCallbackInfo& args) { wrap->InvokeQueued(UV_ECANCELED, "Canceled because of SSL destruction"); // Destroy the SSL structure and friends - wrap->SSLWrap::DestroySSL(); + wrap->DestroySSL(); wrap->enc_in_ = nullptr; wrap->enc_out_ = nullptr; @@ -895,18 +2031,41 @@ void TLSWrap::Initialize(Local target, static_cast(ReadOnly | DontDelete)); AsyncWrap::AddWrapMethods(env, t, AsyncWrap::kFlagHasReset); + StreamBase::AddMethods(env, t); env->SetProtoMethod(t, "receive", Receive); env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode); env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); env->SetProtoMethod(t, "destroySSL", DestroySSL); env->SetProtoMethod(t, "enableCertCb", EnableCertCb); - - StreamBase::AddMethods(env, t); - SSLWrap::AddMethods(env, t); - env->SetProtoMethod(t, "getServername", GetServername); env->SetProtoMethod(t, "setServername", SetServername); + env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate); + env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished); + env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished); + env->SetProtoMethodNoSideEffect(t, "getSession", GetSession); + env->SetProtoMethod(t, "setSession", SetSession); + env->SetProtoMethod(t, "loadSession", LoadSession); + env->SetProtoMethodNoSideEffect(t, "isSessionReused", IsSessionReused); + env->SetProtoMethodNoSideEffect(t, "verifyError", VerifyError); + env->SetProtoMethodNoSideEffect(t, "getCurrentCipher", GetCurrentCipher); + env->SetProtoMethod(t, "endParser", EndParser); + env->SetProtoMethod(t, "certCbDone", CertCbDone); + env->SetProtoMethod(t, "renegotiate", Renegotiate); + env->SetProtoMethodNoSideEffect(t, "getTLSTicket", GetTLSTicket); + env->SetProtoMethod(t, "newSessionDone", NewSessionDone); + env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); + env->SetProtoMethod(t, "requestOCSP", RequestOCSP); + env->SetProtoMethodNoSideEffect(t, "getEphemeralKeyInfo", + GetEphemeralKeyInfo); + env->SetProtoMethodNoSideEffect(t, "getProtocol", GetProtocol); + env->SetProtoMethodNoSideEffect(t, "getALPNNegotiatedProtocol", + GetALPNNegotiatedProto); + env->SetProtoMethod(t, "setALPNProtocols", SetALPNProtocols); + +#ifdef SSL_set_max_send_fragment + env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment); +#endif // SSL_set_max_send_fragment env->set_tls_wrap_constructor_function(t->GetFunction()); diff --git a/src/tls_wrap.h b/src/tls_wrap.h index aea8568b11b51c..4486d10fe87f24 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -25,7 +25,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node.h" -#include "node_crypto.h" // SSLWrap +#include "node_crypto.h" #include "async_wrap.h" #include "env.h" @@ -45,8 +45,11 @@ class SecureContext; class NodeBIO; } +using SSLSessionPointer = DeleteFnPtr; + +int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx); + class TLSWrap : public AsyncWrap, - public crypto::SSLWrap, public StreamBase, public StreamListener { public: @@ -76,6 +79,17 @@ class TLSWrap : public AsyncWrap, void NewSessionDoneCb(); + enum Kind { + kClient, + kServer + }; + + inline void enable_session_callbacks() { session_callbacks_ = true; } + inline bool is_server() const { return kind_ == kServer; } + inline bool is_client() const { return kind_ == kClient; } + inline bool is_waiting_new_session() const { return new_session_wait_; } + inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + void MemoryInfo(MemoryTracker* tracker) const override; ADD_MEMORY_INFO_NAME(TLSWrap) @@ -144,6 +158,84 @@ class TLSWrap : public AsyncWrap, static void SetServername(const v8::FunctionCallbackInfo& args); static int SelectSNIContextCallback(SSL* s, int* ad, void* arg); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + // Size allocated by OpenSSL: one for SSL structure, one for SSL3_STATE and + // some for buffers. + // NOTE: Actually it is much more than this + static const int64_t kExternalSize = + sizeof(SSL) + sizeof(SSL3_STATE) + 42 * 1024; +#else + // OpenSSL 1.1.0 has opaque structures. This is an estimate based on the size + // as of OpenSSL 1.1.0f. + static const int64_t kExternalSize = 4448 + 1024 + 42 * 1024; +#endif + + static void ConfigureSecureContext(crypto::SecureContext* sc); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + static SSL_SESSION* GetSessionCallback(SSL* s, + unsigned char* key, + int len, + int* copy); +#else + static SSL_SESSION* GetSessionCallback(SSL* s, + const unsigned char* key, + int len, + int* copy); +#endif + static int NewSessionCallback(SSL* s, SSL_SESSION* sess); + static void OnClientHello( + void* arg, + const crypto::ClientHelloParser::ClientHello& hello); + + static void GetPeerCertificate( + const v8::FunctionCallbackInfo& args); + static void GetFinished(const v8::FunctionCallbackInfo& args); + static void GetPeerFinished(const v8::FunctionCallbackInfo& args); + static void GetSession(const v8::FunctionCallbackInfo& args); + static void SetSession(const v8::FunctionCallbackInfo& args); + static void LoadSession(const v8::FunctionCallbackInfo& args); + static void IsSessionReused(const v8::FunctionCallbackInfo& args); + static void IsInitFinished(const v8::FunctionCallbackInfo& args); + static void VerifyError(const v8::FunctionCallbackInfo& args); + static void GetCurrentCipher(const v8::FunctionCallbackInfo& args); + static void EndParser(const v8::FunctionCallbackInfo& args); + static void CertCbDone(const v8::FunctionCallbackInfo& args); + static void Renegotiate(const v8::FunctionCallbackInfo& args); + static void Shutdown(const v8::FunctionCallbackInfo& args); + static void GetTLSTicket(const v8::FunctionCallbackInfo& args); + static void NewSessionDone(const v8::FunctionCallbackInfo& args); + static void SetOCSPResponse(const v8::FunctionCallbackInfo& args); + static void RequestOCSP(const v8::FunctionCallbackInfo& args); + static void GetEphemeralKeyInfo( + const v8::FunctionCallbackInfo& args); + static void GetProtocol(const v8::FunctionCallbackInfo& args); + +#ifdef SSL_set_max_send_fragment + static void SetMaxSendFragment( + const v8::FunctionCallbackInfo& args); +#endif // SSL_set_max_send_fragment + + static void GetALPNNegotiatedProto( + const v8::FunctionCallbackInfo& args); + static void SetALPNProtocols(const v8::FunctionCallbackInfo& args); + static int SelectALPNCallback(SSL* s, + const unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* arg); + static int TLSExtStatusCallback(SSL* s, void* arg); + static int SSLCertCallback(SSL* s, void* arg); + + typedef void (*CertCb)(void* arg); + + void DestroySSL(); + void WaitForCertCb(CertCb cb, void* arg); + void SetSNIContext(crypto::SecureContext* sc); + int SetCACerts(crypto::SecureContext* sc); + inline Environment* ssl_env() const { return env_; } + crypto::SecureContext* sc_; BIO* enc_in_ = nullptr; BIO* enc_out_ = nullptr; @@ -157,11 +249,27 @@ class TLSWrap : public AsyncWrap, bool shutdown_; std::string error_; int cycle_depth_; + Environment* const env_; + Kind kind_; + SSLSessionPointer next_sess_; + crypto::SSLPointer ssl_; + bool session_callbacks_; + bool new_session_wait_; + crypto::ClientHelloParser hello_parser_; + Persistent ocsp_response_; + Persistent sni_context_; + + // SSL_set_cert_cb + CertCb cert_cb_; + void* cert_cb_arg_; + bool cert_cb_running_; // If true - delivered EOF to the js-land, either after `close_notify`, or // after the `UV_EOF` on socket. bool eof_; + friend class crypto::SecureContext; + private: static void GetWriteQueueSize( const v8::FunctionCallbackInfo& info); From 6bc325e1e12079cb935130551c564af4bab8c535 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Wed, 15 Aug 2018 16:50:57 -0400 Subject: [PATCH 2/2] squash!: remove headers for non-existent methods --- src/tls_wrap.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 4486d10fe87f24..ed2ac270911cc0 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -196,13 +196,11 @@ class TLSWrap : public AsyncWrap, static void SetSession(const v8::FunctionCallbackInfo& args); static void LoadSession(const v8::FunctionCallbackInfo& args); static void IsSessionReused(const v8::FunctionCallbackInfo& args); - static void IsInitFinished(const v8::FunctionCallbackInfo& args); static void VerifyError(const v8::FunctionCallbackInfo& args); static void GetCurrentCipher(const v8::FunctionCallbackInfo& args); static void EndParser(const v8::FunctionCallbackInfo& args); static void CertCbDone(const v8::FunctionCallbackInfo& args); static void Renegotiate(const v8::FunctionCallbackInfo& args); - static void Shutdown(const v8::FunctionCallbackInfo& args); static void GetTLSTicket(const v8::FunctionCallbackInfo& args); static void NewSessionDone(const v8::FunctionCallbackInfo& args); static void SetOCSPResponse(const v8::FunctionCallbackInfo& args);