From f32cc672fd1bf3012427f0a2f4188e47ca45f4aa Mon Sep 17 00:00:00 2001 From: Tomas Hrcka Date: Tue, 5 Apr 2016 17:02:03 +0200 Subject: [PATCH] pllm --- doc/api/tls.markdown | 5 ++ src/node_constants.cc | 7 ++ src/node_crypto.cc | 241 +++++++++++++++++++++++++++++++++++++++++++------- src/node_crypto.h | 18 +++- src/tls_wrap.cc | 10 ++- 5 files changed, 247 insertions(+), 34 deletions(-) diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 2d23e3f..f2f0c57 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -130,6 +130,11 @@ handshake extensions allowing you: * NPN - to use one TLS server for multiple protocols (HTTP, SPDY) * SNI - to use one TLS server for multiple hostnames with different SSL certificates. + **NOTE**: dueto a design flaw in node **SNI cannot be + used on the server side**, even so all parameters in related functions are + accepted for compatibility reasons. And thus the related events will not + fire unless one aranges this explicitly. This may change, when the OS + provides OpenSSL v1.0.2 or better and node gets linked to this version. ## Perfect Forward Secrecy diff --git a/src/node_constants.cc b/src/node_constants.cc index 51c2ee8..ca8dae4 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -12,8 +12,13 @@ #include #if HAVE_OPENSSL +# include +# include +#ifndef OPENSSL_NO_EC # include +#endif # include + # ifndef OPENSSL_NO_ENGINE # include # endif // !OPENSSL_NO_ENGINE @@ -961,12 +966,14 @@ void DefineOpenSSLConstants(Local target) { #if HAVE_OPENSSL // NOTE: These are not defines +# ifndef OPENSSL_NO_EC NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED); NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_UNCOMPRESSED); NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_HYBRID); #endif +#endif } void DefineSystemConstants(Local target) { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 382a42f..a9570fe 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -34,6 +34,83 @@ #define OPENSSL_CONST #endif +#ifndef SSL_get_server_tmp_key +/* + 1.0.2 SSL_get_server_tmp_key(s, pk) "backport". BAD HACK!!! + NOTE: This imports "foreign" knowledge and thus will break, when SESS_CERT + or CERT_PKEY change, which is definitely the case for the later for + all OpenSSL lib vers != 1.0.1. So don't try to bind to something else! + */ +# define SSL_PKEY_NUM 8 +typedef struct cert_pkey_st { + X509 *x509; + EVP_PKEY *privatekey; + /* Digest to use when signing */ + const EVP_MD *digest; +} CERT_PKEY; + +typedef struct sess_cert_st { + STACK_OF(X509) *cert_chain; /* as received from peer (not for SSL2) */ + /* The 'peer_...' members are used only by clients. */ + int peer_cert_type; + CERT_PKEY *peer_key; /* points to an element of peer_pkeys (never + * NULL!) */ + CERT_PKEY peer_pkeys[SSL_PKEY_NUM]; + /* + * Obviously we don't have the private keys of these, so maybe we + * shouldn't even use the CERT_PKEY type here. + */ +# ifndef OPENSSL_NO_RSA + RSA *peer_rsa_tmp; /* not used for SSL 2 */ +# endif +# ifndef OPENSSL_NO_DH + DH *peer_dh_tmp; /* not used for SSL 2 */ +# endif +# ifndef OPENSSL_NO_ECDH + EC_KEY *peer_ecdh_tmp; +# endif + int references; /* actually always 1 at the moment */ +} SESS_CERT; + +static long SSL_get_server_tmp_key(SSL *s, void *parg) { + if (s->server || !s->session || !s->session->sess_cert) + return 0; + else { + SESS_CERT *sc; + EVP_PKEY *ptmp; + int rv = 0; + sc = s->session->sess_cert; +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DH) && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDH) + if (!sc->peer_rsa_tmp && !sc->peer_dh_tmp && !sc->peer_ecdh_tmp) + return 0; +#endif + ptmp = EVP_PKEY_new(); + if (!ptmp) + return 0; + if (0) ; +#ifndef OPENSSL_NO_RSA + else if (sc->peer_rsa_tmp) + rv = EVP_PKEY_set1_RSA(ptmp, sc->peer_rsa_tmp); +#endif +#ifndef OPENSSL_NO_DH + else if (sc->peer_dh_tmp) + rv = EVP_PKEY_set1_DH(ptmp, sc->peer_dh_tmp); +#endif +#ifndef OPENSSL_NO_ECDH + else if (sc->peer_ecdh_tmp) + rv = EVP_PKEY_set1_EC_KEY(ptmp, sc->peer_ecdh_tmp); +#endif + if (rv) { + *(EVP_PKEY **)parg = ptmp; + return 1; + } + EVP_PKEY_free(ptmp); + return 0; + } +} +#endif /* SSL_get_server_tmp_key */ + + #define THROW_AND_RETURN_IF_NOT_STRING_OR_BUFFER(val) \ do { \ if (!Buffer::HasInstance(val) && !val->IsString()) { \ @@ -164,7 +241,11 @@ template int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg); #endif template void SSLWrap::DestroySSL(); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L template int SSLWrap::SSLCertCallback(SSL* s, void* arg); +#else +template int SSLWrap::SSLCertCallback(SSL* s, X509 **x509, EVP_PKEY **pkey); +#endif template void SSLWrap::WaitForCertCb(CertCb cb, void* arg); @@ -258,8 +339,23 @@ inline void CheckEntropy() { } } +#ifndef NODE_FIPS_MODE +#define NODE_FIPS_MODE 0 +#endif bool EntropySource(unsigned char* buffer, size_t length) { +#ifdef OPENSSL_FIPS + // This call is important for FIPS Capable OpenSSL lib, otherwise + // RAND_status() will hang. Works for non-FIPS capable libs, too. + int err = FIPS_mode(); + // Shouldn't this be a CLI switch? + if (err != NODE_FIPS_MODE && !FIPS_mode_set(NODE_FIPS_MODE)) { + err = ERR_get_error(); + fprintf(stderr, "openssl fips failed: %s\n", ERR_error_string(err, NULL)); + UNREACHABLE(); + } +#endif // OPENSSL_FIPS + // Ensure that OpenSSL's PRNG is properly seeded. CheckEntropy(); // RAND_bytes() can return 0 to indicate that the entropy data is not truly @@ -281,8 +377,12 @@ void SecureContext::Initialize(Environment* env, Local target) { env->SetProtoMethod(t, "addCRL", SecureContext::AddCRL); env->SetProtoMethod(t, "addRootCerts", SecureContext::AddRootCerts); env->SetProtoMethod(t, "setCiphers", SecureContext::SetCiphers); + #ifndef OPENSSL_NO_ECDH env->SetProtoMethod(t, "setECDHCurve", SecureContext::SetECDHCurve); + #endif + #ifndef OPENSSL_NO_DH env->SetProtoMethod(t, "setDHParam", SecureContext::SetDHParam); + #endif env->SetProtoMethod(t, "setOptions", SecureContext::SetOptions); env->SetProtoMethod(t, "setSessionIdContext", SecureContext::SetSessionIdContext); @@ -528,10 +628,20 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, for (int i = 0; i < sk_X509_num(extra_certs); i++) { X509* ca = sk_X509_value(extra_certs, i); - - // NOTE: Increments reference count on `ca` - r = SSL_CTX_add1_chain_cert(ctx, ca); - +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // If ctx->cert->key != NULL create ctx->cert->key->chain if not + // already there, push 'ca' to this chain and finally increment the ca + // reference count by 1 (this is the diff between *_add1_* and *_add0_* + // - the later increments by 0 ;-)) and return 1. Otherwise or if + // something fails in between, return 0. + r = SSL_CTX_add1_chain_cert(ctx, ca); +#else + // Create ctx->extra_certs if not already there, just push 'ca' to this + // chain and return 1. If something fails, return 0. + // NOTE: 1.0.1- does not support multiple certs having its own chain in + // a single context. There is just one: extra_chain! + r = SSL_CTX_add_extra_chain_cert(ctx, ca); +#endif if (!r) { ret = 0; *issuer = nullptr; @@ -797,7 +907,7 @@ void SecureContext::SetCiphers(const FunctionCallbackInfo& args) { SSL_CTX_set_cipher_list(sc->ctx_, *ciphers); } - +#ifndef OPENSSL_NO_ECDH void SecureContext::SetECDHCurve(const FunctionCallbackInfo& args) { SecureContext* sc = Unwrap(args.Holder()); Environment* env = sc->env(); @@ -822,8 +932,9 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo& args) { EC_KEY_free(ecdh); } +#endif - +#ifndef OPENSSL_NO_DH void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { SecureContext* sc = Unwrap(args.This()); Environment* env = sc->env(); @@ -861,7 +972,7 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { if (!r) return env->ThrowTypeError("Error setting temp DH parameter"); } - +#endif void SecureContext::SetOptions(const FunctionCallbackInfo& args) { SecureContext* sc = Unwrap(args.Holder()); @@ -2087,7 +2198,12 @@ void SSLWrap::WaitForCertCb(CertCb cb, void* arg) { template +#if OPENSSL_VERSION_NUMBER >= 0x10002000L int SSLWrap::SSLCertCallback(SSL* s, void* arg) { +#else +/* NOTE: For now this callback gets usually never called dueto design flaws */ +int SSLWrap::SSLCertCallback(SSL* s, X509 **x509, EVP_PKEY **pkey) { +#endif Base* w = static_cast(SSL_get_app_data(s)); if (!w->is_server()) @@ -2159,19 +2275,53 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { w->sni_context_.Reset(env->isolate(), ctx); int rv; + X509* x509; + EVP_PKEY* pkey; + STACK_OF(X509)* chain; // NOTE: reference count is not increased by this API methods - X509* x509 = SSL_CTX_get0_certificate(sc->ctx_); - EVP_PKEY* pkey = SSL_CTX_get0_privatekey(sc->ctx_); - STACK_OF(X509)* chain; +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + x509 = SSL_CTX_get0_certificate(sc->ctx_); + pkey = SSL_CTX_get0_privatekey(sc->ctx_); + rv = SSL_CTX_get0_chain_certs(sc->ctx_, &chain); +#else + SSL *ssl = SSL_new(sc->ctx_); + rv = SSL_CTX_get_extra_chain_certs(sc->ctx_, &chain); + if (ssl) { + SSL_set_connect_state(ssl); /* just cleanup/reset state - cheap */ + x509 = SSL_get_certificate(ssl); + SSL_free(ssl); + } else { + x509 = NULL; + pkey = NULL; + } +#endif - rv = SSL_CTX_get0_chain_certs(sc->ctx_, &chain); if (rv) - rv = SSL_use_certificate(w->ssl_, x509); + rv = SSL_use_certificate(w->ssl_, x509); if (rv) - rv = SSL_use_PrivateKey(w->ssl_, pkey); - if (rv && chain != nullptr) - rv = SSL_set1_chain(w->ssl_, chain); + rv = SSL_use_PrivateKey(w->ssl_, pkey); + if (rv && chain != nullptr) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // replaces w->ssl_->cert->key->chain with a copy of the given chain, + // which is allowed to be NULL + rv = SSL_set1_chain(w->ssl_, chain); +#else + // just replace the extra chain with the given chain - 1.0.1- does not + // support chain per cert + SSL_CTX_clear_extra_chain_certs(w->ssl_->ctx); + if (chain != NULL) { + int i; + SSL_CTX* ctx = w->ssl_->ctx; + for (i = 0; i < sk_X509_num(chain); i++) { + // can't do anything: however others might be ok and still + // satisfy requirements + SSL_CTX_add_extra_chain_cert(ctx, sk_X509_value(chain,i)); + } + } + rv = 1; +#endif + } if (rv) rv = w->SetCACerts(sc); if (!rv) { @@ -2233,10 +2383,15 @@ void SSLWrap::SetSNIContext(SecureContext* sc) { template int SSLWrap::SetCACerts(SecureContext* sc) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L int err = SSL_set1_verify_cert_store(ssl_, SSL_CTX_get_cert_store(sc->ctx_)); if (err != 1) return err; - +#else + // there is no ssl_->cert->verify_store in <= 1.0.1. So no need to: free the + // old store, set the new one to it and increment its ref count. +#endif + STACK_OF(X509_NAME)* list = SSL_dup_CA_list( SSL_CTX_get_client_CA_list(sc->ctx_)); @@ -2513,8 +2668,11 @@ inline int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { // Server does not need to check the whitelist. SSL* ssl = static_cast( X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); - +#if OPENSSL_VERSION_NUMBER >= 0x10002000L if (SSL_is_server(ssl)) +#else + if (ssl->server) +#endif return 1; // Client needs to check if the server cert is listed in the @@ -2594,8 +2752,21 @@ void Connection::New(const FunctionCallbackInfo& args) { SSL_set_info_callback(conn->ssl_, SSLInfoCallback); InitNPN(sc); - +#if OPENSSL_VERSION_NUMBER >= 0x10002000L SSL_set_cert_cb(conn->ssl_, SSLWrap::SSLCertCallback, conn); +#else + /* 1.0.1 and less have no general cert callback. The closest for a client is + SSL_CTX_set_client_cert_cb(conn->ssl_->ctx, SSLWrap::SSLCertCallback); + but on the client it is not needed/used by this implementation. Since this + the SSLCertCallback actually calls lib/_tls_wrap.js:oncertcb(), which in + turn loadSNI() and this the actual SNICallback of the JSON object, sets + the context and finally requestOCSP() and certCbDone(). Not sure, why + the SNICallback of the JSON object, doesn't get invoked via + SelectSNIContextCallback_() - design flaw because lets do 2 things at once + (i.e. do SNICallback and attach the certs ca chain), however, this means + no server side support for the SNI TLS/OCSP_state extension anymore. + */ +#endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (is_server) { @@ -3689,7 +3860,8 @@ SignBase::Error Sign::SignFinal(const char* key_pem, if (pkey == nullptr || 0 != ERR_peek_error()) goto exit; -#ifdef NODE_FIPS_MODE +#ifdef OPENSSL_FIPS + if (FIPS_mode()) { /* Validate DSA2 parameters from FIPS 186-4 */ if (EVP_PKEY_DSA == pkey->type) { size_t L = BN_num_bits(pkey->pkey.dsa->p); @@ -3710,7 +3882,8 @@ SignBase::Error Sign::SignFinal(const char* key_pem, goto exit; } } -#endif // NODE_FIPS_MODE +} +#endif // OPENSSL_FIPS if (EVP_SignFinal(&mdctx_, *sig, sig_len, pkey)) fatal = false; @@ -4125,7 +4298,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { delete[] out_value; } - +#ifndef OPENSSL_NO_DH void DiffieHellman::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(New); @@ -4177,7 +4350,7 @@ void DiffieHellman::Initialize(Environment* env, Local target) { target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"), t2->GetFunction()); } - +#endif bool DiffieHellman::Init(int primeLength, int g) { dh = DH_new(); @@ -4517,7 +4690,7 @@ bool DiffieHellman::VerifyContext() { return true; } - +#ifndef OPENSSL_NO_ECDH void ECDH::Initialize(Environment* env, Local target) { HandleScope scope(env->isolate()); @@ -4535,7 +4708,7 @@ void ECDH::Initialize(Environment* env, Local target) { target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH"), t->GetFunction()); } - +#endif void ECDH::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -5240,7 +5413,7 @@ void GetHashes(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(ctx.arr); } - +#ifndef OPENSSL_NO_EC void GetCurves(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); const size_t num_curves = EC_get_builtin_curves(nullptr, 0); @@ -5265,7 +5438,7 @@ void GetCurves(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(arr); } - +#endif void Certificate::Initialize(Environment* env, Local target) { HandleScope scope(env->isolate()); @@ -5462,13 +5635,15 @@ void InitCryptoOnce() { CRYPTO_set_locking_callback(crypto_lock_cb); CRYPTO_THREADID_set_callback(crypto_threadid_cb); -#ifdef NODE_FIPS_MODE - if (!FIPS_mode_set(1)) { - int err = ERR_get_error(); +#ifdef OPENSSL_FIPS + int err = FIPS_mode(); + // Shouldn't this be a CLI switch? + if (err != NODE_FIPS_MODE && !FIPS_mode_set(NODE_FIPS_MODE)) { + err = ERR_get_error(); fprintf(stderr, "openssl fips failed: %s\n", ERR_error_string(err, NULL)); UNREACHABLE(); } -#endif // NODE_FIPS_MODE +#endif // OPENSSL_FIPS // Turn off compression. Saves memory and protects against CRIME attacks. @@ -5544,8 +5719,12 @@ void InitCrypto(Local target, SecureContext::Initialize(env, target); Connection::Initialize(env, target); CipherBase::Initialize(env, target); +#ifndef OPENSSL_NO_EC DiffieHellman::Initialize(env, target); +#endif +#ifndef OPENSSL_NO_ECDH ECDH::Initialize(env, target); +#endif Hmac::Initialize(env, target); Hash::Initialize(env, target); Sign::Initialize(env, target); @@ -5560,7 +5739,9 @@ void InitCrypto(Local target, env->SetMethod(target, "getSSLCiphers", GetSSLCiphers); env->SetMethod(target, "getCiphers", GetCiphers); env->SetMethod(target, "getHashes", GetHashes); +#ifndef OPENSSL_NO_EC env->SetMethod(target, "getCurves", GetCurves); +#endif env->SetMethod(target, "publicEncrypt", PublicKeyCipher::Cipher +#ifndef OPENSSL_NO_EC #include +#endif +#ifndef OPENSSL_NO_ECDH #include +#endif #ifndef OPENSSL_NO_ENGINE # include #endif // !OPENSSL_NO_ENGINE @@ -101,8 +105,12 @@ class SecureContext : public BaseObject { static void AddCRL(const v8::FunctionCallbackInfo& args); static void AddRootCerts(const v8::FunctionCallbackInfo& args); static void SetCiphers(const v8::FunctionCallbackInfo& args); +#ifndef OPENSSL_NO_ECDH static void SetECDHCurve(const v8::FunctionCallbackInfo& args); +#endif +#ifndef OPENSSL_NO_DH static void SetDHParam(const v8::FunctionCallbackInfo& args); +#endif static void SetOptions(const v8::FunctionCallbackInfo& args); static void SetSessionIdContext( const v8::FunctionCallbackInfo& args); @@ -273,7 +281,11 @@ class SSLWrap { void* arg); #endif // OPENSSL_NPN_NEGOTIATED static int TLSExtStatusCallback(SSL* s, void* arg); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L static int SSLCertCallback(SSL* s, void* arg); +#else + static int SSLCertCallback(SSL* s, X509 **x509, EVP_PKEY **pkey); +#endif static void SSLGetter(v8::Local property, const v8::PropertyCallbackInfo& info); @@ -645,7 +657,7 @@ class PublicKeyCipher { EVP_PKEY_cipher_t EVP_PKEY_cipher> static void Cipher(const v8::FunctionCallbackInfo& args); }; - +#ifndef OPENSSL_NO_DH class DiffieHellman : public BaseObject { public: ~DiffieHellman() override { @@ -691,7 +703,9 @@ class DiffieHellman : public BaseObject { int verifyError_; DH* dh; }; +#endif +#ifndef OPENSSL_NO_ECDH class ECDH : public BaseObject { public: ~ECDH() override { @@ -727,7 +741,7 @@ class ECDH : public BaseObject { EC_KEY* key_; const EC_GROUP* group_; }; - +#endif class Certificate : public AsyncWrap { public: static void Initialize(Environment* env, v8::Local target); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 85730b3..47441bd 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -140,9 +140,15 @@ void TLSWrap::InitSSL() { #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB InitNPN(sc_); - +#if OPENSSL_VERSION_NUMBER >= 0x10002000L SSL_set_cert_cb(ssl_, SSLWrap::SSLCertCallback, this); - +#else + /* 1.0.1 and less have at most for the client side the function + SSL_CTX_set_client_cert_cb(ssl_->ctx, SSLWrap::SSLCertCallback); + but on the client it is not needed/used by this implementation. + For more info see comments in src/node_crypto.cc Connection::New(). + */ +#endif if (is_server()) { SSL_set_accept_state(ssl_); } else if (is_client()) { -- 2.5.5