|
|
@ -143,6 +143,7 @@ template int SSLWrap<TLSCallbacks>::SelectNextProtoCallback( |
|
|
|
unsigned int inlen, |
|
|
|
void* arg); |
|
|
|
#endif |
|
|
|
template int SSLWrap<TLSCallbacks>::TLSExtStatusCallback(SSL* s, void* arg); |
|
|
|
|
|
|
|
|
|
|
|
static void crypto_threadid_cb(CRYPTO_THREADID* tid) { |
|
|
@ -283,6 +284,12 @@ void SecureContext::Initialize(Environment* env, Handle<Object> target) { |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "getTicketKeys", SecureContext::GetTicketKeys); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "setTicketKeys", SecureContext::SetTicketKeys); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, |
|
|
|
"getCertificate", |
|
|
|
SecureContext::GetCertificate<true>); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, |
|
|
|
"getIssuer", |
|
|
|
SecureContext::GetCertificate<false>); |
|
|
|
|
|
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "SecureContext"), |
|
|
|
t->GetFunction()); |
|
|
@ -469,7 +476,10 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) { |
|
|
|
// sent to the peer in the Certificate message.
|
|
|
|
//
|
|
|
|
// Taken from OpenSSL - editted for style.
|
|
|
|
int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, BIO *in) { |
|
|
|
int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, |
|
|
|
BIO *in, |
|
|
|
X509** cert, |
|
|
|
X509** issuer) { |
|
|
|
int ret = 0; |
|
|
|
X509 *x = NULL; |
|
|
|
|
|
|
@ -511,6 +521,11 @@ int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, BIO *in) { |
|
|
|
// added to the chain (while we must free the main
|
|
|
|
// certificate, since its reference count is increased
|
|
|
|
// by SSL_CTX_use_certificate).
|
|
|
|
|
|
|
|
// Find issuer
|
|
|
|
if (*issuer != NULL || X509_check_issued(ca, x) != X509_V_OK) |
|
|
|
continue; |
|
|
|
*issuer = ca; |
|
|
|
} |
|
|
|
|
|
|
|
// When the while loop ends, it's usually just EOF.
|
|
|
@ -524,9 +539,31 @@ int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, BIO *in) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Try getting issuer from a cert store
|
|
|
|
if (ret) { |
|
|
|
if (*issuer == NULL) { |
|
|
|
X509_STORE* store = SSL_CTX_get_cert_store(ctx); |
|
|
|
X509_STORE_CTX store_ctx; |
|
|
|
|
|
|
|
ret = X509_STORE_CTX_init(&store_ctx, store, NULL, NULL); |
|
|
|
if (!ret) |
|
|
|
goto end; |
|
|
|
|
|
|
|
ret = X509_STORE_CTX_get1_issuer(issuer, &store_ctx, x); |
|
|
|
X509_STORE_CTX_cleanup(&store_ctx); |
|
|
|
|
|
|
|
ret = ret < 0 ? 0 : 1; |
|
|
|
// NOTE: get_cert_store doesn't increment reference count,
|
|
|
|
// no need to free `store`
|
|
|
|
} else { |
|
|
|
// Increment issuer reference count
|
|
|
|
CRYPTO_add(&(*issuer)->references, 1, CRYPTO_LOCK_X509); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
end: |
|
|
|
if (x != NULL) |
|
|
|
X509_free(x); |
|
|
|
*cert = x; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
@ -545,7 +582,10 @@ void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) { |
|
|
|
if (!bio) |
|
|
|
return; |
|
|
|
|
|
|
|
int rv = SSL_CTX_use_certificate_chain(sc->ctx_, bio); |
|
|
|
int rv = SSL_CTX_use_certificate_chain(sc->ctx_, |
|
|
|
bio, |
|
|
|
&sc->cert_, |
|
|
|
&sc->issuer_); |
|
|
|
|
|
|
|
BIO_free_all(bio); |
|
|
|
|
|
|
@ -881,6 +921,30 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <bool primary> |
|
|
|
void SecureContext::GetCertificate(const FunctionCallbackInfo<Value>& args) { |
|
|
|
HandleScope scope(args.GetIsolate()); |
|
|
|
SecureContext* wrap = Unwrap<SecureContext>(args.Holder()); |
|
|
|
Environment* env = wrap->env(); |
|
|
|
X509* cert; |
|
|
|
|
|
|
|
if (primary) |
|
|
|
cert = wrap->cert_; |
|
|
|
else |
|
|
|
cert = wrap->issuer_; |
|
|
|
if (cert == NULL) |
|
|
|
return args.GetReturnValue().Set(Null(env->isolate())); |
|
|
|
|
|
|
|
int size = i2d_X509(cert, NULL); |
|
|
|
Local<Object> buff = Buffer::New(env, size); |
|
|
|
unsigned char* serialized = reinterpret_cast<unsigned char*>( |
|
|
|
Buffer::Data(buff)); |
|
|
|
i2d_X509(cert, &serialized); |
|
|
|
|
|
|
|
args.GetReturnValue().Set(buff); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <class Base> |
|
|
|
void SSLWrap<Base>::AddMethods(Environment* env, Handle<FunctionTemplate> t) { |
|
|
|
HandleScope scope(env->isolate()); |
|
|
@ -898,6 +962,8 @@ void SSLWrap<Base>::AddMethods(Environment* env, Handle<FunctionTemplate> t) { |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "getTLSTicket", GetTLSTicket); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "newSessionDone", NewSessionDone); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "setOCSPResponse", SetOCSPResponse); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "requestOCSP", RequestOCSP); |
|
|
|
|
|
|
|
#ifdef SSL_set_max_send_fragment |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "setMaxSendFragment", SetMaxSendFragment); |
|
|
@ -926,6 +992,12 @@ void SSLWrap<Base>::InitNPN(SecureContext* sc, Base* base) { |
|
|
|
SSL_CTX_set_next_proto_select_cb(sc->ctx_, SelectNextProtoCallback, base); |
|
|
|
#endif // OPENSSL_NPN_NEGOTIATED
|
|
|
|
} |
|
|
|
|
|
|
|
#ifdef NODE__HAVE_TLSEXT_STATUS_CB |
|
|
|
// OCSP stapling
|
|
|
|
SSL_CTX_set_tlsext_status_cb(sc->ctx_, TLSExtStatusCallback); |
|
|
|
SSL_CTX_set_tlsext_status_arg(sc->ctx_, base); |
|
|
|
#endif // NODE__HAVE_TLSEXT_STATUS_CB
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1001,6 +1073,8 @@ void SSLWrap<Base>::OnClientHello(void* arg, |
|
|
|
} |
|
|
|
hello_obj->Set(env->tls_ticket_string(), |
|
|
|
Boolean::New(env->isolate(), hello.has_ticket())); |
|
|
|
hello_obj->Set(env->ocsp_request_string(), |
|
|
|
Boolean::New(env->isolate(), hello.ocsp_request())); |
|
|
|
|
|
|
|
Local<Value> argv[] = { hello_obj }; |
|
|
|
w->MakeCallback(env->onclienthello_string(), ARRAY_SIZE(argv), argv); |
|
|
@ -1042,8 +1116,15 @@ void SSLWrap<Base>::GetPeerCertificate( |
|
|
|
} |
|
|
|
(void) BIO_reset(bio); |
|
|
|
|
|
|
|
int index = X509_get_ext_by_NID(peer_cert, NID_subject_alt_name, -1); |
|
|
|
if (index >= 0) { |
|
|
|
int nids[] = { NID_subject_alt_name, NID_info_access }; |
|
|
|
Local<String> keys[] = { env->subjectaltname_string(), |
|
|
|
env->infoaccess_string() }; |
|
|
|
CHECK_EQ(ARRAY_SIZE(nids), ARRAY_SIZE(keys)); |
|
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(nids); i++) { |
|
|
|
int index = X509_get_ext_by_NID(peer_cert, nids[i], -1); |
|
|
|
if (index < 0) |
|
|
|
continue; |
|
|
|
|
|
|
|
X509_EXTENSION* ext; |
|
|
|
int rv; |
|
|
|
|
|
|
@ -1054,7 +1135,7 @@ void SSLWrap<Base>::GetPeerCertificate( |
|
|
|
assert(rv == 1); |
|
|
|
|
|
|
|
BIO_get_mem_ptr(bio, &mem); |
|
|
|
info->Set(env->subjectaltname_string(), |
|
|
|
info->Set(keys[i], |
|
|
|
OneByteString(args.GetIsolate(), mem->data, mem->length)); |
|
|
|
|
|
|
|
(void) BIO_reset(bio); |
|
|
@ -1316,6 +1397,34 @@ void SSLWrap<Base>::NewSessionDone(const FunctionCallbackInfo<Value>& args) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <class Base> |
|
|
|
void SSLWrap<Base>::SetOCSPResponse( |
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) { |
|
|
|
#ifdef NODE__HAVE_TLSEXT_STATUS_CB |
|
|
|
HandleScope scope(args.GetIsolate()); |
|
|
|
|
|
|
|
Base* w = Unwrap<Base>(args.Holder()); |
|
|
|
if (args.Length() < 1 || !Buffer::HasInstance(args[0])) |
|
|
|
return w->env()->ThrowTypeError("Must give a Buffer as first argument"); |
|
|
|
|
|
|
|
w->ocsp_response_.Reset(args.GetIsolate(), args[0].As<Object>()); |
|
|
|
#endif // NODE__HAVE_TLSEXT_STATUS_CB
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <class Base> |
|
|
|
void SSLWrap<Base>::RequestOCSP( |
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) { |
|
|
|
#ifdef NODE__HAVE_TLSEXT_STATUS_CB |
|
|
|
HandleScope scope(args.GetIsolate()); |
|
|
|
|
|
|
|
Base* w = Unwrap<Base>(args.Holder()); |
|
|
|
|
|
|
|
SSL_set_tlsext_status_type(w->ssl_, TLSEXT_STATUSTYPE_ocsp); |
|
|
|
#endif // NODE__HAVE_TLSEXT_STATUS_CB
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#ifdef SSL_set_max_send_fragment |
|
|
|
template <class Base> |
|
|
|
void SSLWrap<Base>::SetMaxSendFragment( |
|
|
@ -1547,6 +1656,55 @@ void SSLWrap<Base>::SetNPNProtocols(const FunctionCallbackInfo<Value>& args) { |
|
|
|
#endif // OPENSSL_NPN_NEGOTIATED
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NODE__HAVE_TLSEXT_STATUS_CB |
|
|
|
template <class Base> |
|
|
|
int SSLWrap<Base>::TLSExtStatusCallback(SSL* s, void* arg) { |
|
|
|
Base* w = static_cast<Base*>(arg); |
|
|
|
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<Value> arg; |
|
|
|
if (resp == NULL) { |
|
|
|
arg = Null(env->isolate()); |
|
|
|
} else { |
|
|
|
arg = Buffer::New( |
|
|
|
env, |
|
|
|
reinterpret_cast<char*>(const_cast<unsigned char*>(resp)), |
|
|
|
len); |
|
|
|
} |
|
|
|
|
|
|
|
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<Object> 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 = reinterpret_cast<char*>(malloc(len)); |
|
|
|
assert(data != NULL); |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif // NODE__HAVE_TLSEXT_STATUS_CB
|
|
|
|
|
|
|
|
|
|
|
|
void Connection::OnClientHelloParseEnd(void* arg) { |
|
|
|
Connection* conn = static_cast<Connection*>(arg); |
|
|
|
|
|
|
|