0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/src/tls_wrap.cc
Fedor Indutny 9180140231 _stream_wrap: prevent use after free in TLS
Queued write requests should be invoked on handle close, otherwise the
"consumer" might be already destroyed when the write callbacks of the
"consumed" handle will be invoked. Same applies to the shutdown
requests.

Make sure to "move" away socket from server to not break the
`connections` counter in `net.js`. Otherwise it might not call `close`
callback, or call it too early.

Fix: https://github.com/iojs/io.js/issues/1696
PR-URL: https://github.com/nodejs/io.js/pull/1910
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
2015-06-30 20:09:22 -07:00

908 lines
22 KiB
C++

#include "tls_wrap.h"
#include "async-wrap.h"
#include "async-wrap-inl.h"
#include "node_buffer.h" // Buffer
#include "node_crypto.h" // SecureContext
#include "node_crypto_bio.h" // NodeBIO
#include "node_crypto_clienthello.h" // ClientHelloParser
#include "node_crypto_clienthello-inl.h"
#include "node_wrap.h" // WithGenericStream
#include "node_counters.h"
#include "node_internals.h"
#include "stream_base.h"
#include "stream_base-inl.h"
#include "util.h"
#include "util-inl.h"
namespace node {
using crypto::SSLWrap;
using crypto::SecureContext;
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Handle;
using v8::Integer;
using v8::Local;
using v8::Null;
using v8::Object;
using v8::String;
using v8::Value;
TLSWrap::TLSWrap(Environment* env,
Kind kind,
StreamBase* stream,
SecureContext* sc)
: SSLWrap<TLSWrap>(env, sc, kind),
StreamBase(env),
AsyncWrap(env,
env->tls_wrap_constructor_function()->NewInstance(),
AsyncWrap::PROVIDER_TLSWRAP),
sc_(sc),
stream_(stream),
enc_in_(nullptr),
enc_out_(nullptr),
clear_in_(nullptr),
write_size_(0),
started_(false),
established_(false),
shutdown_(false),
error_(nullptr),
cycle_depth_(0),
eof_(false) {
node::Wrap(object(), this);
MakeWeak(this);
// We've our own session callbacks
SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap<TLSWrap>::GetSessionCallback);
SSL_CTX_sess_set_new_cb(sc_->ctx_, SSLWrap<TLSWrap>::NewSessionCallback);
stream_->Consume();
stream_->set_after_write_cb(OnAfterWriteImpl, this);
stream_->set_alloc_cb(OnAllocImpl, this);
stream_->set_read_cb(OnReadImpl, this);
set_alloc_cb(OnAllocSelf, this);
set_read_cb(OnReadSelf, this);
InitSSL();
}
TLSWrap::~TLSWrap() {
enc_in_ = nullptr;
enc_out_ = nullptr;
delete clear_in_;
clear_in_ = nullptr;
sc_ = nullptr;
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
sni_context_.Reset();
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
ClearError();
}
void TLSWrap::MakePending() {
write_item_queue_.MoveBack(&pending_write_items_);
}
bool TLSWrap::InvokeQueued(int status) {
if (pending_write_items_.IsEmpty())
return false;
// Process old queue
WriteItemList queue;
pending_write_items_.MoveBack(&queue);
while (WriteItem* wi = queue.PopFront()) {
wi->w_->Done(status);
delete wi;
}
return true;
}
void TLSWrap::NewSessionDoneCb() {
Cycle();
}
void TLSWrap::InitSSL() {
// Initialize SSL
enc_in_ = NodeBIO::New();
enc_out_ = NodeBIO::New();
NodeBIO::FromBIO(enc_in_)->AssignEnvironment(env());
NodeBIO::FromBIO(enc_out_)->AssignEnvironment(env());
SSL_set_bio(ssl_, enc_in_, enc_out_);
// NOTE: This could be overriden in SetVerifyMode
SSL_set_verify(ssl_, SSL_VERIFY_NONE, crypto::VerifyCallback);
#ifdef SSL_MODE_RELEASE_BUFFERS
long mode = SSL_get_mode(ssl_);
SSL_set_mode(ssl_, mode | SSL_MODE_RELEASE_BUFFERS);
#endif // SSL_MODE_RELEASE_BUFFERS
SSL_set_app_data(ssl_, this);
SSL_set_info_callback(ssl_, SSLInfoCallback);
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
if (is_server()) {
SSL_CTX_set_tlsext_servername_callback(sc_->ctx_, SelectSNIContextCallback);
}
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
InitNPN(sc_);
SSL_set_cert_cb(ssl_, SSLWrap<TLSWrap>::SSLCertCallback, this);
if (is_server()) {
SSL_set_accept_state(ssl_);
} else if (is_client()) {
// Enough space for server response (hello, cert)
NodeBIO::FromBIO(enc_in_)->set_initial(kInitialClientBufferLength);
SSL_set_connect_state(ssl_);
} else {
// Unexpected
abort();
}
// Initialize ring for queud clear data
clear_in_ = new NodeBIO();
clear_in_->AssignEnvironment(env());
}
void TLSWrap::Wrap(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args.Length() < 1 || !args[0]->IsObject()) {
return env->ThrowTypeError(
"First argument should be a StreamWrap instance");
}
if (args.Length() < 2 || !args[1]->IsObject()) {
return env->ThrowTypeError(
"Second argument should be a SecureContext instance");
}
if (args.Length() < 3 || !args[2]->IsBoolean())
return env->ThrowTypeError("Third argument should be boolean");
Local<Object> stream_obj = args[0].As<Object>();
Local<Object> sc = args[1].As<Object>();
Kind kind = args[2]->IsTrue() ? SSLWrap<TLSWrap>::kServer :
SSLWrap<TLSWrap>::kClient;
StreamBase* stream = nullptr;
WITH_GENERIC_STREAM(env, stream_obj, {
stream = wrap;
});
CHECK_NE(stream, nullptr);
TLSWrap* res = new TLSWrap(env, kind, stream, Unwrap<SecureContext>(sc));
args.GetReturnValue().Set(res->object());
}
void TLSWrap::Receive(const FunctionCallbackInfo<Value>& args) {
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
CHECK(Buffer::HasInstance(args[0]));
char* data = Buffer::Data(args[0]);
size_t len = Buffer::Length(args[0]);
uv_buf_t buf;
// Copy given buffer entirely or partiall if handle becomes closed
while (len > 0 && wrap->IsAlive() && !wrap->IsClosing()) {
wrap->stream_->OnAlloc(len, &buf);
size_t copy = buf.len > len ? len : buf.len;
memcpy(buf.base, data, copy);
buf.len = copy;
wrap->stream_->OnRead(buf.len, &buf);
data += copy;
len -= copy;
}
}
void TLSWrap::Start(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
if (wrap->started_)
return env->ThrowError("Already started.");
wrap->started_ = true;
// Send ClientHello handshake
CHECK(wrap->is_client());
wrap->ClearOut();
wrap->EncOut();
}
void TLSWrap::SSLInfoCallback(const SSL* ssl_, int where, int ret) {
if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)))
return;
// Be compatible with older versions of OpenSSL. SSL_get_app_data() wants
// a non-const SSL* in OpenSSL <= 0.9.7e.
SSL* ssl = const_cast<SSL*>(ssl_);
TLSWrap* c = static_cast<TLSWrap*>(SSL_get_app_data(ssl));
Environment* env = c->env();
Local<Object> object = c->object();
if (where & SSL_CB_HANDSHAKE_START) {
Local<Value> callback = object->Get(env->onhandshakestart_string());
if (callback->IsFunction()) {
c->MakeCallback(callback.As<Function>(), 0, nullptr);
}
}
if (where & SSL_CB_HANDSHAKE_DONE) {
c->established_ = true;
Local<Value> callback = object->Get(env->onhandshakedone_string());
if (callback->IsFunction()) {
c->MakeCallback(callback.As<Function>(), 0, nullptr);
}
}
}
void TLSWrap::EncOut() {
// Ignore cycling data if ClientHello wasn't yet parsed
if (!hello_parser_.IsEnded())
return;
// Write in progress
if (write_size_ != 0)
return;
// Wait for `newSession` callback to be invoked
if (is_waiting_new_session())
return;
// Split-off queue
if (established_ && !write_item_queue_.IsEmpty())
MakePending();
if (ssl_ == nullptr)
return;
// No data to write
if (BIO_pending(enc_out_) == 0) {
if (clear_in_->Length() == 0)
InvokeQueued(0);
return;
}
char* data[kSimultaneousBufferCount];
size_t size[ARRAY_SIZE(data)];
size_t count = ARRAY_SIZE(data);
write_size_ = NodeBIO::FromBIO(enc_out_)->PeekMultiple(data, size, &count);
CHECK(write_size_ != 0 && count != 0);
Local<Object> req_wrap_obj =
env()->write_wrap_constructor_function()->NewInstance();
WriteWrap* write_req = WriteWrap::New(env(),
req_wrap_obj,
this,
EncOutCb);
uv_buf_t buf[ARRAY_SIZE(data)];
for (size_t i = 0; i < count; i++)
buf[i] = uv_buf_init(data[i], size[i]);
int err = stream_->DoWrite(write_req, buf, count, nullptr);
// Ignore errors, this should be already handled in js
if (err) {
write_req->Dispose();
InvokeQueued(err);
} else {
NODE_COUNT_NET_BYTES_SENT(write_size_);
}
}
void TLSWrap::EncOutCb(WriteWrap* req_wrap, int status) {
TLSWrap* wrap = req_wrap->wrap()->Cast<TLSWrap>();
req_wrap->Dispose();
// We should not be getting here after `DestroySSL`, because all queued writes
// must be invoked with UV_ECANCELED
CHECK_NE(wrap->ssl_, nullptr);
// Handle error
if (status) {
// Ignore errors after shutdown
if (wrap->shutdown_)
return;
// Notify about error
wrap->InvokeQueued(status);
return;
}
// Commit
NodeBIO::FromBIO(wrap->enc_out_)->Read(nullptr, wrap->write_size_);
// Ensure that the progress will be made and `InvokeQueued` will be called.
wrap->ClearIn();
// Try writing more data
wrap->write_size_ = 0;
wrap->EncOut();
}
Local<Value> TLSWrap::GetSSLError(int status, int* err, const char** msg) {
EscapableHandleScope scope(env()->isolate());
// ssl_ is already destroyed in reading EOF by close notify alert.
if (ssl_ == nullptr)
return Local<Value>();
*err = SSL_get_error(ssl_, status);
switch (*err) {
case SSL_ERROR_NONE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_X509_LOOKUP:
break;
case SSL_ERROR_ZERO_RETURN:
return scope.Escape(env()->zero_return_string());
break;
default:
{
CHECK(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL);
BIO* bio = BIO_new(BIO_s_mem());
ERR_print_errors(bio);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
Local<String> message =
OneByteString(env()->isolate(), mem->data, mem->length);
Local<Value> exception = Exception::Error(message);
if (msg != nullptr) {
CHECK_EQ(*msg, nullptr);
char* const buf = new char[mem->length + 1];
memcpy(buf, mem->data, mem->length);
buf[mem->length] = '\0';
*msg = buf;
}
BIO_free_all(bio);
return scope.Escape(exception);
}
}
return Local<Value>();
}
void TLSWrap::ClearOut() {
// Ignore cycling data if ClientHello wasn't yet parsed
if (!hello_parser_.IsEnded())
return;
// No reads after EOF
if (eof_)
return;
if (ssl_ == nullptr)
return;
char out[kClearOutChunkSize];
int read;
for (;;) {
read = SSL_read(ssl_, out, sizeof(out));
if (read <= 0)
break;
while (read > 0) {
int avail = read;
uv_buf_t buf;
OnAlloc(avail, &buf);
if (static_cast<int>(buf.len) < avail)
avail = buf.len;
memcpy(buf.base, out, avail);
OnRead(avail, &buf);
read -= avail;
}
}
int flags = SSL_get_shutdown(ssl_);
if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) {
eof_ = true;
OnRead(UV_EOF, nullptr);
}
// We need to check whether an error occurred or the connection was
// shutdown cleanly (SSL_ERROR_ZERO_RETURN) even when read == 0.
// See iojs#1642 and SSL_read(3SSL) for details.
if (read <= 0) {
int err;
Local<Value> arg = GetSSLError(read, &err, nullptr);
// Ignore ZERO_RETURN after EOF, it is basically not a error
if (err == SSL_ERROR_ZERO_RETURN && eof_)
return;
if (!arg.IsEmpty()) {
// When TLS Alert are stored in wbio,
// it should be flushed to socket before destroyed.
if (BIO_pending(enc_out_) != 0)
EncOut();
MakeCallback(env()->onerror_string(), 1, &arg);
}
}
}
bool TLSWrap::ClearIn() {
// Ignore cycling data if ClientHello wasn't yet parsed
if (!hello_parser_.IsEnded())
return false;
if (ssl_ == nullptr)
return false;
int written = 0;
while (clear_in_->Length() > 0) {
size_t avail = 0;
char* data = clear_in_->Peek(&avail);
written = SSL_write(ssl_, data, avail);
CHECK(written == -1 || written == static_cast<int>(avail));
if (written == -1)
break;
clear_in_->Read(nullptr, avail);
}
// All written
if (clear_in_->Length() == 0) {
CHECK_GE(written, 0);
return true;
}
// Error or partial write
int err;
Local<Value> arg = GetSSLError(written, &err, &error_);
if (!arg.IsEmpty()) {
MakePending();
if (!InvokeQueued(UV_EPROTO))
ClearError();
clear_in_->Reset();
}
return false;
}
void* TLSWrap::Cast() {
return reinterpret_cast<void*>(this);
}
AsyncWrap* TLSWrap::GetAsyncWrap() {
return static_cast<AsyncWrap*>(this);
}
bool TLSWrap::IsIPCPipe() {
return stream_->IsIPCPipe();
}
int TLSWrap::GetFD() {
return stream_->GetFD();
}
bool TLSWrap::IsAlive() {
return ssl_ != nullptr && stream_->IsAlive();
}
bool TLSWrap::IsClosing() {
return stream_->IsClosing();
}
int TLSWrap::ReadStart() {
return stream_->ReadStart();
}
int TLSWrap::ReadStop() {
return stream_->ReadStop();
}
const char* TLSWrap::Error() const {
return error_;
}
void TLSWrap::ClearError() {
delete[] error_;
error_ = nullptr;
}
int TLSWrap::DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
uv_stream_t* send_handle) {
CHECK_EQ(send_handle, nullptr);
CHECK_NE(ssl_, nullptr);
bool empty = true;
// Empty writes should not go through encryption process
size_t i;
for (i = 0; i < count; i++)
if (bufs[i].len > 0) {
empty = false;
break;
}
if (empty) {
ClearOut();
// However if there any data that should be written to socket,
// callback should not be invoked immediately
if (BIO_pending(enc_out_) == 0)
return stream_->DoWrite(w, bufs, count, send_handle);
}
// Queue callback to execute it on next tick
write_item_queue_.PushBack(new WriteItem(w));
w->Dispatched();
// Write queued data
if (empty) {
EncOut();
return 0;
}
// Process enqueued data first
if (!ClearIn()) {
// If there're still data to process - enqueue current one
for (i = 0; i < count; i++)
clear_in_->Write(bufs[i].base, bufs[i].len);
return 0;
}
if (ssl_ == nullptr)
return UV_EPROTO;
int written = 0;
for (i = 0; i < count; i++) {
written = SSL_write(ssl_, bufs[i].base, bufs[i].len);
CHECK(written == -1 || written == static_cast<int>(bufs[i].len));
if (written == -1)
break;
}
if (i != count) {
int err;
Local<Value> arg = GetSSLError(written, &err, &error_);
if (!arg.IsEmpty())
return UV_EPROTO;
// No errors, queue rest
for (; i < count; i++)
clear_in_->Write(bufs[i].base, bufs[i].len);
}
// Try writing data immediately
EncOut();
return 0;
}
void TLSWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) {
// Intentionally empty
}
void TLSWrap::OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) {
TLSWrap* wrap = static_cast<TLSWrap*>(ctx);
if (wrap->ssl_ == nullptr) {
*buf = uv_buf_init(nullptr, 0);
return;
}
size_t size = 0;
buf->base = NodeBIO::FromBIO(wrap->enc_in_)->PeekWritable(&size);
buf->len = size;
}
void TLSWrap::OnReadImpl(ssize_t nread,
const uv_buf_t* buf,
uv_handle_type pending,
void* ctx) {
TLSWrap* wrap = static_cast<TLSWrap*>(ctx);
wrap->DoRead(nread, buf, pending);
}
void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) {
buf->base = static_cast<char*>(malloc(suggested_size));
CHECK_NE(buf->base, nullptr);
buf->len = suggested_size;
}
void TLSWrap::OnReadSelf(ssize_t nread,
const uv_buf_t* buf,
uv_handle_type pending,
void* ctx) {
TLSWrap* wrap = static_cast<TLSWrap*>(ctx);
Local<Object> buf_obj;
if (buf != nullptr)
buf_obj = Buffer::Use(wrap->env(), buf->base, buf->len);
wrap->EmitData(nread, buf_obj, Local<Object>());
}
void TLSWrap::DoRead(ssize_t nread,
const uv_buf_t* buf,
uv_handle_type pending) {
if (nread < 0) {
// Error should be emitted only after all data was read
ClearOut();
// Ignore EOF if received close_notify
if (nread == UV_EOF) {
if (eof_)
return;
eof_ = true;
}
OnRead(nread, nullptr);
return;
}
// Only client connections can receive data
if (ssl_ == nullptr) {
OnRead(UV_EPROTO, nullptr);
return;
}
// Commit read data
NodeBIO* enc_in = NodeBIO::FromBIO(enc_in_);
enc_in->Commit(nread);
// Parse ClientHello first
if (!hello_parser_.IsEnded()) {
size_t avail = 0;
uint8_t* data = reinterpret_cast<uint8_t*>(enc_in->Peek(&avail));
CHECK(avail == 0 || data != nullptr);
return hello_parser_.Parse(data, avail);
}
// Cycle OpenSSL's state
Cycle();
}
int TLSWrap::DoShutdown(ShutdownWrap* req_wrap) {
if (ssl_ != nullptr && SSL_shutdown(ssl_) == 0)
SSL_shutdown(ssl_);
shutdown_ = true;
EncOut();
return stream_->DoShutdown(req_wrap);
}
void TLSWrap::SetVerifyMode(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
if (args.Length() < 2 || !args[0]->IsBoolean() || !args[1]->IsBoolean())
return env->ThrowTypeError("Bad arguments, expected two booleans");
if (wrap->ssl_ == nullptr)
return env->ThrowTypeError("SetVerifyMode after destroySSL");
int verify_mode;
if (wrap->is_server()) {
bool request_cert = args[0]->IsTrue();
if (!request_cert) {
// Note reject_unauthorized ignored.
verify_mode = SSL_VERIFY_NONE;
} else {
bool reject_unauthorized = args[1]->IsTrue();
verify_mode = SSL_VERIFY_PEER;
if (reject_unauthorized)
verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
} else {
// Note request_cert and reject_unauthorized are ignored for clients.
verify_mode = SSL_VERIFY_NONE;
}
// Always allow a connection. We'll reject in javascript.
SSL_set_verify(wrap->ssl_, verify_mode, crypto::VerifyCallback);
}
void TLSWrap::EnableSessionCallbacks(
const FunctionCallbackInfo<Value>& args) {
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
if (wrap->ssl_ == nullptr) {
return wrap->env()->ThrowTypeError(
"EnableSessionCallbacks after destroySSL");
}
wrap->enable_session_callbacks();
NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength);
wrap->hello_parser_.Start(SSLWrap<TLSWrap>::OnClientHello,
OnClientHelloParseEnd,
wrap);
}
void TLSWrap::DestroySSL(const FunctionCallbackInfo<Value>& args) {
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
// Move all writes to pending
wrap->MakePending();
// And destroy
wrap->InvokeQueued(UV_ECANCELED);
// Destroy the SSL structure and friends
wrap->SSLWrap<TLSWrap>::DestroySSL();
delete wrap->clear_in_;
wrap->clear_in_ = nullptr;
}
void TLSWrap::EnableCertCb(const FunctionCallbackInfo<Value>& args) {
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
wrap->WaitForCertCb(OnClientHelloParseEnd, wrap);
}
void TLSWrap::OnClientHelloParseEnd(void* arg) {
TLSWrap* c = static_cast<TLSWrap*>(arg);
c->Cycle();
}
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
void TLSWrap::GetServername(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
CHECK_NE(wrap->ssl_, nullptr);
const char* servername = SSL_get_servername(wrap->ssl_,
TLSEXT_NAMETYPE_host_name);
if (servername != nullptr) {
args.GetReturnValue().Set(OneByteString(env->isolate(), servername));
} else {
args.GetReturnValue().Set(false);
}
}
void TLSWrap::SetServername(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
if (args.Length() < 1 || !args[0]->IsString())
return env->ThrowTypeError("First argument should be a string");
if (wrap->started_)
return env->ThrowError("Already started.");
if (!wrap->is_client())
return;
CHECK_NE(wrap->ssl_, nullptr);
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
node::Utf8Value servername(env->isolate(), args[0].As<String>());
SSL_set_tlsext_host_name(wrap->ssl_, *servername);
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
}
int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
TLSWrap* p = static_cast<TLSWrap*>(SSL_get_app_data(s));
Environment* env = p->env();
const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
if (servername == nullptr)
return SSL_TLSEXT_ERR_OK;
// Call the SNI callback and use its return value as context
Local<Object> object = p->object();
Local<Value> ctx = object->Get(env->sni_context_string());
// Not an object, probably undefined or null
if (!ctx->IsObject())
return SSL_TLSEXT_ERR_NOACK;
Local<FunctionTemplate> cons = env->secure_context_constructor_template();
if (!cons->HasInstance(ctx)) {
// Failure: incorrect SNI context object
Local<Value> err = Exception::TypeError(env->sni_context_err_string());
p->MakeCallback(env->onerror_string(), 1, &err);
return SSL_TLSEXT_ERR_NOACK;
}
p->sni_context_.Reset();
p->sni_context_.Reset(env->isolate(), ctx);
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
InitNPN(sc);
SSL_set_SSL_CTX(s, sc->ctx_);
return SSL_TLSEXT_ERR_OK;
}
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
void TLSWrap::Initialize(Handle<Object> target,
Handle<Value> unused,
Handle<Context> context) {
Environment* env = Environment::GetCurrent(context);
env->SetMethod(target, "wrap", TLSWrap::Wrap);
Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate());
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"));
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<TLSWrap>(env, t, StreamBase::kFlagHasWritev);
SSLWrap<TLSWrap>::AddMethods(env, t);
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
env->SetProtoMethod(t, "getServername", GetServername);
env->SetProtoMethod(t, "setServername", SetServername);
#endif // SSL_CRT_SET_TLSEXT_SERVERNAME_CB
env->set_tls_wrap_constructor_template(t);
env->set_tls_wrap_constructor_function(t->GetFunction());
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"),
t->GetFunction());
}
} // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(tls_wrap, node::TLSWrap::Initialize)