summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/env.h4
-rw-r--r--src/node.cc7
-rw-r--r--src/node_crypto.cc170
-rw-r--r--src/node_crypto.h28
-rw-r--r--src/node_crypto_clienthello.cc13
-rw-r--r--src/node_crypto_clienthello.h15
6 files changed, 229 insertions, 8 deletions
diff --git a/src/env.h b/src/env.h
index 95e1df6e59..44054149a4 100644
--- a/src/env.h
+++ b/src/env.h
@@ -111,6 +111,7 @@ namespace node {
V(hostmaster_string, "hostmaster") \
V(ignore_string, "ignore") \
V(immediate_callback_string, "_immediateCallback") \
+ V(infoaccess_string, "infoAccess") \
V(inherit_string, "inherit") \
V(ino_string, "ino") \
V(input_string, "input") \
@@ -136,6 +137,7 @@ namespace node {
V(nice_string, "nice") \
V(nlink_string, "nlink") \
V(nsname_string, "nsname") \
+ V(ocsp_request_string, "OCSPRequest") \
V(offset_string, "offset") \
V(onchange_string, "onchange") \
V(onclienthello_string, "onclienthello") \
@@ -149,6 +151,7 @@ namespace node {
V(onmessage_string, "onmessage") \
V(onnewsession_string, "onnewsession") \
V(onnewsessiondone_string, "onnewsessiondone") \
+ V(onocspresponse_string, "onocspresponse") \
V(onread_string, "onread") \
V(onselect_string, "onselect") \
V(onsignal_string, "onsignal") \
@@ -207,6 +210,7 @@ namespace node {
V(timestamp_string, "timestamp") \
V(title_string, "title") \
V(tls_npn_string, "tls_npn") \
+ V(tls_ocsp_string, "tls_ocsp") \
V(tls_sni_string, "tls_sni") \
V(tls_string, "tls") \
V(tls_ticket_string, "tlsTicket") \
diff --git a/src/node.cc b/src/node.cc
index afa4f2b030..51fd6a8188 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -2447,6 +2447,13 @@ static Handle<Object> GetFeatures(Environment* env) {
#endif
obj->Set(env->tls_sni_string(), tls_sni);
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
+ Local<Boolean> tls_ocsp = True(env->isolate());
+#else
+ Local<Boolean> tls_ocsp = False(env->isolate());
+#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
+ obj->Set(env->tls_ocsp_string(), tls_ocsp);
+
obj->Set(env->tls_string(),
Boolean::New(env->isolate(), get_builtin_module("crypto") != NULL));
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 407ad52833..0d685c9f51 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -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);
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 6605dfdf69..7645eb281a 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -53,6 +53,9 @@
#define EVP_F_EVP_DECRYPTFINAL 101
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
+# define NODE__HAVE_TLSEXT_STATUS_CB
+#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
namespace node {
namespace crypto {
@@ -74,6 +77,8 @@ class SecureContext : public BaseObject {
X509_STORE* ca_store_;
SSL_CTX* ctx_;
+ X509* cert_;
+ X509* issuer_;
static const int kMaxSessionSize = 10 * 1024;
@@ -98,10 +103,15 @@ class SecureContext : public BaseObject {
static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
+ template <bool primary>
+ static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
+
SecureContext(Environment* env, v8::Local<v8::Object> wrap)
: BaseObject(env, wrap),
ca_store_(NULL),
- ctx_(NULL) {
+ ctx_(NULL),
+ cert_(NULL),
+ issuer_(NULL) {
MakeWeak<SecureContext>(this);
}
@@ -115,8 +125,14 @@ class SecureContext : public BaseObject {
ctx_->cert_store = NULL;
}
SSL_CTX_free(ctx_);
+ if (cert_ != NULL)
+ X509_free(cert_);
+ if (issuer_ != NULL)
+ X509_free(issuer_);
ctx_ = NULL;
ca_store_ = NULL;
+ cert_ = NULL;
+ issuer_ = NULL;
} else {
assert(ca_store_ == NULL);
}
@@ -157,6 +173,9 @@ class SSLWrap {
npn_protos_.Reset();
selected_npn_proto_.Reset();
#endif
+#ifdef NODE__HAVE_TLSEXT_STATUS_CB
+ ocsp_response_.Reset();
+#endif // NODE__HAVE_TLSEXT_STATUS_CB
}
inline SSL* ssl() const { return ssl_; }
@@ -191,6 +210,8 @@ class SSLWrap {
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);
static void NewSessionDone(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void SetOCSPResponse(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RequestOCSP(const v8::FunctionCallbackInfo<v8::Value>& args);
#ifdef SSL_set_max_send_fragment
static void SetMaxSendFragment(
@@ -212,6 +233,7 @@ class SSLWrap {
unsigned int inlen,
void* arg);
#endif // OPENSSL_NPN_NEGOTIATED
+ static int TLSExtStatusCallback(SSL* s, void* arg);
inline Environment* ssl_env() const {
return env_;
@@ -225,6 +247,10 @@ class SSLWrap {
bool new_session_wait_;
ClientHelloParser hello_parser_;
+#ifdef NODE__HAVE_TLSEXT_STATUS_CB
+ v8::Persistent<v8::Object> ocsp_response_;
+#endif // NODE__HAVE_TLSEXT_STATUS_CB
+
#ifdef OPENSSL_NPN_NEGOTIATED
v8::Persistent<v8::Object> npn_protos_;
v8::Persistent<v8::Value> selected_npn_proto_;
diff --git a/src/node_crypto_clienthello.cc b/src/node_crypto_clienthello.cc
index b786942529..c1228c79ac 100644
--- a/src/node_crypto_clienthello.cc
+++ b/src/node_crypto_clienthello.cc
@@ -123,6 +123,7 @@ void ClientHelloParser::ParseHeader(const uint8_t* data, size_t avail) {
hello.session_id_ = session_id_;
hello.session_size_ = session_size_;
hello.has_ticket_ = tls_ticket_ != NULL && tls_ticket_size_ != 0;
+ hello.ocsp_request_ = ocsp_request_;
hello.servername_ = servername_;
hello.servername_size_ = servername_size_;
onhello_cb_(cb_arg_, hello);
@@ -159,6 +160,18 @@ void ClientHelloParser::ParseExtension(ClientHelloParser::ExtensionType type,
}
}
break;
+ case kStatusRequest:
+ // We are ignoring any data, just indicating the presence of extension
+ if (len < kMinStatusRequestSize)
+ return;
+
+ // Unknown type, ignore it
+ if (data[0] != kStatusRequestOCSP)
+ break;
+
+ // Ignore extensions, they won't work with caching on backend anyway
+ ocsp_request_ = 1;
+ break;
case kTLSSessionTicket:
tls_ticket_size_ = len;
tls_ticket_ = data + len;
diff --git a/src/node_crypto_clienthello.h b/src/node_crypto_clienthello.h
index 4301d9bb1c..3ebcead0c3 100644
--- a/src/node_crypto_clienthello.h
+++ b/src/node_crypto_clienthello.h
@@ -34,7 +34,14 @@ class ClientHelloParser {
ClientHelloParser() : state_(kEnded),
onhello_cb_(NULL),
onend_cb_(NULL),
- cb_arg_(NULL) {
+ cb_arg_(NULL),
+ session_size_(0),
+ session_id_(NULL),
+ servername_size_(0),
+ servername_(NULL),
+ ocsp_request_(0),
+ tls_ticket_size_(0),
+ tls_ticket_(NULL) {
Reset();
}
@@ -48,6 +55,7 @@ class ClientHelloParser {
inline bool has_ticket() const { return has_ticket_; }
inline uint8_t servername_size() const { return servername_size_; }
inline const uint8_t* servername() const { return servername_; }
+ inline int ocsp_request() const { return ocsp_request_; }
private:
uint8_t session_size_;
@@ -55,6 +63,7 @@ class ClientHelloParser {
bool has_ticket_;
uint8_t servername_size_;
const uint8_t* servername_;
+ int ocsp_request_;
friend class ClientHelloParser;
};
@@ -76,6 +85,8 @@ class ClientHelloParser {
static const size_t kMaxTLSFrameLen = 16 * 1024 + 5;
static const size_t kMaxSSLExFrameLen = 32 * 1024;
static const uint8_t kServernameHostname = 0;
+ static const uint8_t kStatusRequestOCSP = 1;
+ static const size_t kMinStatusRequestSize = 5;
enum ParseState {
kWaiting,
@@ -99,6 +110,7 @@ class ClientHelloParser {
enum ExtensionType {
kServerName = 0,
+ kStatusRequest = 5,
kTLSSessionTicket = 35
};
@@ -123,6 +135,7 @@ class ClientHelloParser {
const uint8_t* session_id_;
uint16_t servername_size_;
const uint8_t* servername_;
+ uint8_t ocsp_request_;
uint16_t tls_ticket_size_;
const uint8_t* tls_ticket_;
};