tls: reset NPN callbacks after SNI
authorFedor Indutny <fedor.indutny@gmail.com>
Fri, 22 Nov 2013 14:33:50 +0000 (18:33 +0400)
committerFedor Indutny <fedor.indutny@gmail.com>
Mon, 2 Dec 2013 10:48:14 +0000 (14:48 +0400)
SNI callback selects a new SSL_CTX for the connection, which doesn't
have NPN callbacks set up.

src/node_crypto.cc
src/node_crypto.h
test/simple/test-tls-npn-server-client.js

index e3ece08..3e77918 100644 (file)
@@ -1189,6 +1189,7 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
         p->sniContext_ = Persistent<Value>::New(ret);
         SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(
                                 Local<Object>::Cast(ret));
+        p->InitNPN(sc, true);
         SSL_set_SSL_CTX(s, sc->ctx_);
       } else {
         return SSL_TLSEXT_ERR_NOACK;
@@ -1223,20 +1224,7 @@ Handle<Value> Connection::New(const Arguments& args) {
 
   if (is_server) SSL_set_info_callback(p->ssl_, SSLInfoCallback);
 
-#ifdef OPENSSL_NPN_NEGOTIATED
-  if (is_server) {
-    // Server should advertise NPN protocols
-    SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
-                                          AdvertiseNextProtoCallback_,
-                                          NULL);
-  } else {
-    // Client should select protocol from advertised
-    // If server supports NPN
-    SSL_CTX_set_next_proto_select_cb(sc->ctx_,
-                                     SelectNextProtoCallback_,
-                                     NULL);
-  }
-#endif
+  p->InitNPN(sc, is_server);
 
 #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
   if (is_server) {
@@ -1980,6 +1968,24 @@ Handle<Value> Connection::Close(const Arguments& args) {
   return True();
 }
 
+
+void Connection::InitNPN(SecureContext* sc, bool is_server) {
+#ifdef OPENSSL_NPN_NEGOTIATED
+  if (is_server) {
+    // Server should advertise NPN protocols
+    SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
+                                          AdvertiseNextProtoCallback_,
+                                          NULL);
+  } else {
+    // Client should select protocol from advertised
+    // If server supports NPN
+    SSL_CTX_set_next_proto_select_cb(sc->ctx_,
+                                     SelectNextProtoCallback_,
+                                     NULL);
+  }
+#endif
+}
+
 #ifdef OPENSSL_NPN_NEGOTIATED
 Handle<Value> Connection::GetNegotiatedProto(const Arguments& args) {
   HandleScope scope;
index f1f6334..01a0528 100644 (file)
@@ -190,6 +190,8 @@ class Connection : ObjectWrap {
   static v8::Handle<v8::Value> Start(const v8::Arguments& args);
   static v8::Handle<v8::Value> Close(const v8::Arguments& args);
 
+  static void InitNPN(SecureContext* sc, bool is_server);
+
 #ifdef OPENSSL_NPN_NEGOTIATED
   // NPN
   static v8::Handle<v8::Value> GetNegotiatedProto(const v8::Arguments& args);
index d9e8f91..ef89bd2 100644 (file)
@@ -28,7 +28,8 @@ if (!process.features.tls_npn) {
 var common = require('../common'),
     assert = require('assert'),
     fs = require('fs'),
-    tls = require('tls');
+    tls = require('tls'),
+    crypto = require('crypto');
 
 function filenamePEM(n) {
   return require('path').join(common.fixturesDir, 'keys', n + '.pem');
@@ -42,6 +43,13 @@ var serverOptions = {
   key: loadPEM('agent2-key'),
   cert: loadPEM('agent2-cert'),
   crl: loadPEM('ca2-crl'),
+  SNICallback: function() {
+    return crypto.createCredentials({
+      key: loadPEM('agent2-key'),
+      cert: loadPEM('agent2-cert'),
+      crl: loadPEM('ca2-crl'),
+    }).context;
+  },
   NPNProtocols: ['a', 'b', 'c']
 };