From 2411d250a2789675aa9c902aeea3f0c55b34a4e0 Mon Sep 17 00:00:00 2001 From: Haikel Guemar Date: Tue, 26 Jul 2016 22:50:22 +0200 Subject: [PATCH 2/3] Use openssl 1.0.1 Based on Solaris patches from upstream #2783 https://github.com/nodejs/node/issues/2783 --- doc/api/tls.md | 6 ++ src/node_constants.cc | 5 ++ src/node_crypto.cc | 201 ++++++++++++++++++++++++++++++++++++++++++++++---- src/node_crypto.h | 16 ++++ src/tls_wrap.cc | 8 ++ 5 files changed, 223 insertions(+), 13 deletions(-) diff --git a/doc/api/tls.md b/doc/api/tls.md index b7e5c8f15e2bf4e8604cdd1bc95ea0e98a96b7d6..6b3069f9352ae44d0dab37b3e4b2eebdbc104609 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -113,10 +113,16 @@ handshake extensions: * ALPN/NPN - Allows the use of one TLS server for multiple protocols (HTTP, SPDY, HTTP/2) * SNI - Allows the use of 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. + *Note*: Use of ALPN is recommended over NPN. The NPN extension has never been formally defined or documented and generally not recommended for use. ### Client-initiated renegotiation attack mitigation diff --git a/src/node_constants.cc b/src/node_constants.cc index 750df9c669bad38e0cc05158bd8ef1c90d810f74..d693851c4bb6c247cfda7436c5ea0baf5bebc886 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -12,11 +12,14 @@ #include #include #include #if HAVE_OPENSSL +# include +# ifndef OPENSSL_NO_EC # include +# endif # include # ifndef OPENSSL_NO_ENGINE # include # endif // !OPENSSL_NO_ENGINE #endif @@ -974,16 +977,18 @@ void DefineOpenSSLConstants(Local target) { NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING); #endif #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) { // file access modes NODE_DEFINE_CONSTANT(target, O_RDONLY); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f654dcf60cb42479b06fcd2be96e5bda287606c7..7aaff07fb513945d6272f238814a4cd297da7b0e 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -26,10 +26,86 @@ #include // INT_MAX #include #include #include +#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, prefix) \ do { \ if (!Buffer::HasInstance(val) && !val->IsString()) { \ return env->ThrowTypeError(prefix " must be a string or a buffer"); \ } \ @@ -165,11 +241,15 @@ template int SSLWrap::SelectNextProtoCallback( #ifdef NODE__HAVE_TLSEXT_STATUS_CB 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); #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation template int SSLWrap::SelectALPNCallback( SSL* s, @@ -295,12 +375,16 @@ void SecureContext::Initialize(Environment* env, Local target) { env->SetProtoMethod(t, "setCert", SecureContext::SetCert); env->SetProtoMethod(t, "addCACert", SecureContext::AddCACert); 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); env->SetProtoMethod(t, "setSessionTimeout", SecureContext::SetSessionTimeout); @@ -531,12 +615,24 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, SSL_CTX_clear_extra_chain_certs(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; goto end; @@ -887,10 +983,11 @@ void SecureContext::SetCiphers(const FunctionCallbackInfo& args) { const node::Utf8Value ciphers(args.GetIsolate(), args[0]); SSL_CTX_set_cipher_list(sc->ctx_, *ciphers); } +#ifndef OPENSSL_NO_ECDH void SecureContext::SetECDHCurve(const FunctionCallbackInfo& args) { SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); Environment* env = sc->env(); @@ -914,12 +1011,14 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo& args) { SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(sc->ctx_, ecdh); EC_KEY_free(ecdh); } +#endif +#ifndef OPENSSL_NO_DH void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.This()); Environment* env = sc->env(); ClearErrorOnReturn clear_error_on_return; @@ -954,10 +1053,11 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { DH_free(dh); if (!r) return env->ThrowTypeError("Error setting temp DH parameter"); } +#endif void SecureContext::SetOptions(const FunctionCallbackInfo& args) { SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); @@ -1977,10 +2077,11 @@ void SSLWrap::GetEphemeralKeyInfo( info->Set(env->type_string(), FIXED_ONE_BYTE_STRING(env->isolate(), "DH")); info->Set(env->size_string(), Integer::New(env->isolate(), EVP_PKEY_bits(key))); break; +#ifndef OPENSSL_NO_ECDH case EVP_PKEY_EC: { EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key); int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); @@ -1989,10 +2090,11 @@ void SSLWrap::GetEphemeralKeyInfo( info->Set(env->name_string(), OneByteString(args.GetIsolate(), OBJ_nid2sn(nid))); info->Set(env->size_string(), Integer::New(env->isolate(), EVP_PKEY_bits(key))); } +#endif } EVP_PKEY_free(key); } return args.GetReturnValue().Set(info); @@ -2407,11 +2509,16 @@ void SSLWrap::WaitForCertCb(CertCb cb, void* arg) { cert_cb_arg_ = 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()) return 1; @@ -2476,23 +2583,57 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, ctx.As()); w->sni_context_.Reset(); 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); - if (rv) - rv = SSL_use_PrivateKey(w->ssl_, pkey); - if (rv && chain != nullptr) - rv = SSL_set1_chain(w->ssl_, chain); + if (rv) + rv = SSL_use_certificate(w->ssl_, x509); + if (rv) + 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) { unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) @@ -2552,14 +2693,18 @@ 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_)); // NOTE: `SSL_set_client_CA_list` takes the ownership of `list` SSL_set_client_CA_list(ssl_, list); @@ -2871,11 +3016,15 @@ 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 CHECK_OK; // Client needs to check if the server cert is listed in the // whitelist when it is issued by the specific rootCAs. CheckResult ret = CheckWhitelistedServerCert(ctx); @@ -2954,11 +3103,25 @@ void Connection::New(const FunctionCallbackInfo& args) { if (is_server) 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) { SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_); } else if (args[2]->IsString()) { @@ -4541,10 +4704,11 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(vbuf); delete[] out_value; } +#ifndef OPENSSL_NO_DH void DiffieHellman::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(New); const PropertyAttribute attributes = static_cast(v8::ReadOnly | v8::DontDelete); @@ -4942,12 +5106,14 @@ bool DiffieHellman::VerifyContext() { if (!DH_check(dh, &codes)) return false; verifyError_ = codes; return true; } +#endif +#ifndef OPENSSL_NO_ECDH void ECDH::Initialize(Environment* env, Local target) { HandleScope scope(env->isolate()); Local t = env->NewFunctionTemplate(New); @@ -5173,10 +5339,11 @@ void ECDH::SetPrivateKey(const FunctionCallbackInfo& args) { return env->ThrowError("Failed to set generated public key"); } EC_POINT_free(pub); } +#endif void ECDH::SetPublicKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -5728,10 +5895,11 @@ void GetHashes(const FunctionCallbackInfo& args) { EVP_MD_do_all_sorted(array_push_back, &ctx); 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); Local arr = Array::New(env->isolate(), num_curves); EC_builtin_curve* curves; @@ -5752,10 +5920,11 @@ void GetCurves(const FunctionCallbackInfo& args) { free(curves); } args.GetReturnValue().Set(arr); } +#endif bool VerifySpkac(const char* data, unsigned int len) { bool i = 0; EVP_PKEY* pkey = nullptr; @@ -6063,12 +6232,16 @@ void InitCrypto(Local target, Environment* env = Environment::GetCurrent(context); 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); Verify::Initialize(env, target); @@ -6084,11 +6257,13 @@ void InitCrypto(Local target, env->SetMethod(target, "randomBytes", RandomBytes); env->SetMethod(target, "timingSafeEqual", TimingSafeEqual); 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); env->SetMethod(target, "privateDecrypt", diff --git a/src/node_crypto.h b/src/node_crypto.h index 38f49ba5a05063438372471b4a58036190954cd3..8304e719719dae41757071b4463f8631cb1e442f 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -16,12 +16,16 @@ #include "base-object-inl.h" #include "v8.h" #include +# ifndef OPENSSL_NO_EC #include +# endif +# ifndef OPENSSL_NO_ECDH #include +# endif #ifndef OPENSSL_NO_ENGINE # include #endif // !OPENSSL_NO_ENGINE #include #include @@ -100,12 +104,16 @@ class SecureContext : public BaseObject { static void SetCert(const v8::FunctionCallbackInfo& args); static void AddCACert(const v8::FunctionCallbackInfo& args); 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); static void SetSessionTimeout( const v8::FunctionCallbackInfo& args); @@ -273,11 +281,15 @@ class SSLWrap { unsigned char* outlen, const unsigned char* in, unsigned int inlen, void* arg); 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); void DestroySSL(); void WaitForCertCb(CertCb cb, void* arg); @@ -635,10 +647,11 @@ class PublicKeyCipher { EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init, 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 { if (dh != nullptr) { DH_free(dh); @@ -680,11 +693,13 @@ class DiffieHellman : public BaseObject { bool initialised_; int verifyError_; DH* dh; }; +#endif +# ifndef OPENSSL_NO_ECDH class ECDH : public BaseObject { public: ~ECDH() override { if (key_ != nullptr) EC_KEY_free(key_); @@ -717,10 +732,11 @@ class ECDH : public BaseObject { bool IsKeyValidForCurve(const BIGNUM* private_key); EC_KEY* key_; const EC_GROUP* group_; }; +#endif bool EntropySource(unsigned char* buffer, size_t length); #ifndef OPENSSL_NO_ENGINE void SetEngine(const v8::FunctionCallbackInfo& args); #endif // !OPENSSL_NO_ENGINE diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 8eab962a66f61c3f3020c59f4db472f286856d97..5d741576720b9e3a0fbc50b08f8f3ebd715148dc 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -141,11 +141,19 @@ 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()) { // Enough space for server response (hello, cert) -- 2.13.5