From 91d1a67b8cc249af59929cd1a76629a2614860bd Mon Sep 17 00:00:00 2001 From: Haikel Guemar Date: Tue, 26 Jul 2016 22:50:22 +0200 Subject: [PATCH 2/4] 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 3784210ba7b6c046b39d74b45e44538041d35ae2..3c9d72b8d5ef81d15773aed077bd00d2041c9e93 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 2e6be8df37c345a383d8a78898daf2a147d90630..239eadbac8ea8e601745a63347a8bb301c22d1b1 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 482ec230c0f4500b63b705d705a142e63ff179e5..c5630f30d0bef75ced53b36062bb1f0324dbdb9d 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -22,10 +22,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"); \ } \ @@ -161,11 +237,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, @@ -281,12 +361,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); @@ -514,12 +598,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; @@ -868,10 +964,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(); @@ -895,12 +992,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; @@ -935,10 +1034,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()); @@ -1952,10 +2052,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); @@ -1964,10 +2065,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); @@ -2382,11 +2484,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; @@ -2451,23 +2558,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) @@ -2527,14 +2668,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); @@ -2808,11 +2953,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 1; // Client needs to check if the server cert is listed in the // whitelist when it is issued by the specific rootCAs. CheckResult ret = CheckWhitelistedServerCert(ctx); @@ -2891,11 +3040,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()) { @@ -4476,10 +4639,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); @@ -4877,12 +5041,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); @@ -5106,10 +5272,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); @@ -5659,10 +5826,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; @@ -5683,10 +5851,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; @@ -5995,12 +6164,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); @@ -6016,11 +6189,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 175206c40df58602b0c24d039b8b5a8bb6f56ba3..5ecc43b08d0b4d97311f09271a26f5a735a6e018 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 d1b1aeccdd95b00b3fd0421c08fd7816cd70d182..a6e63d7c1ccc81e6f7d782dffe833234b003de15 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -140,11 +140,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.12.0