Browse Source

tls: fix crash in SNICallback

`tls_wrap.cc` was crashing in an `Unwrap` call, when non
`SecureContext` object was passed to it. Check that the passed object
is a `SecureContext` instance before unwrapping it.

fix #7008
v0.11.12-release
Fedor Indutny 11 years ago
parent
commit
9a60bf3726
  1. 1
      src/env.h
  2. 34
      src/tls_wrap.cc
  3. 69
      test/simple/test-tls-sni-option.js

1
src/env.h

@ -120,6 +120,7 @@ namespace node {
V(should_keep_alive_string, "shouldKeepAlive") \ V(should_keep_alive_string, "shouldKeepAlive") \
V(size_string, "size") \ V(size_string, "size") \
V(smalloc_p_string, "_smalloc_p") \ V(smalloc_p_string, "_smalloc_p") \
V(sni_context_err_string, "Invalid SNI context") \
V(sni_context_string, "sni_context") \ V(sni_context_string, "sni_context") \
V(status_code_string, "statusCode") \ V(status_code_string, "statusCode") \
V(status_message_string, "statusMessage") \ V(status_message_string, "statusMessage") \

34
src/tls_wrap.cc

@ -747,29 +747,37 @@ void TLSCallbacks::SetServername(const FunctionCallbackInfo<Value>& args) {
int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
HandleScope scope(node_isolate);
TLSCallbacks* p = static_cast<TLSCallbacks*>(arg); TLSCallbacks* p = static_cast<TLSCallbacks*>(arg);
Environment* env = p->env(); Environment* env = p->env();
const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
if (servername != NULL) { if (servername == NULL)
// Call the SNI callback and use its return value as context return SSL_TLSEXT_ERR_OK;
Local<Object> object = p->object();
Local<Value> ctx = object->Get(env->sni_context_string());
if (!ctx->IsObject()) HandleScope scope(env->isolate());
return SSL_TLSEXT_ERR_NOACK; // 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());
p->sni_context_.Dispose(); // Not an object, probably undefined or null
p->sni_context_.Reset(node_isolate, ctx); if (!ctx->IsObject())
return SSL_TLSEXT_ERR_NOACK;
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>()); Local<FunctionTemplate> cons = env->secure_context_constructor_template();
InitNPN(sc, p); if (!cons->HasInstance(ctx)) {
SSL_set_SSL_CTX(s, sc->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_.Dispose();
p->sni_context_.Reset(node_isolate, ctx);
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
InitNPN(sc, p);
SSL_set_SSL_CTX(s, sc->ctx_);
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB

69
test/simple/test-tls-sni-option.js

@ -47,10 +47,14 @@ var serverOptions = {
// Just to test asynchronous callback // Just to test asynchronous callback
setTimeout(function() { setTimeout(function() {
if (credentials) if (credentials) {
callback(null, crypto.createCredentials(credentials).context); if (credentials.emptyRegression)
else callback(null, {});
else
callback(null, crypto.createCredentials(credentials).context);
} else {
callback(null, null); callback(null, null);
}
}, 100); }, 100);
} }
}; };
@ -63,6 +67,9 @@ var SNIContexts = {
'b.example.com': { 'b.example.com': {
key: loadPEM('agent3-key'), key: loadPEM('agent3-key'),
cert: loadPEM('agent3-cert') cert: loadPEM('agent3-cert')
},
'c.another.com': {
emptyRegression: true
} }
}; };
@ -89,39 +96,73 @@ var clientsOptions = [{
ca: [loadPEM('ca1-cert')], ca: [loadPEM('ca1-cert')],
servername: 'c.wrong.com', servername: 'c.wrong.com',
rejectUnauthorized: false rejectUnauthorized: false
}, {
port: serverPort,
key: loadPEM('agent3-key'),
cert: loadPEM('agent3-cert'),
ca: [loadPEM('ca1-cert')],
servername: 'c.another.com',
rejectUnauthorized: false
}]; }];
var serverResults = [], var serverResults = [],
clientResults = []; clientResults = [],
serverErrors = [],
clientErrors = [],
serverError,
clientError;
var server = tls.createServer(serverOptions, function(c) { var server = tls.createServer(serverOptions, function(c) {
serverResults.push(c.servername); serverResults.push(c.servername);
}); });
server.on('clientError', function(err) {
serverResults.push(null);
serverError = err.message;
});
server.listen(serverPort, startTest); server.listen(serverPort, startTest);
function startTest() { function startTest() {
function connectClient(options, callback) { function connectClient(i, callback) {
var options = clientsOptions[i];
clientError = null;
serverError = null;
var client = tls.connect(options, function() { var client = tls.connect(options, function() {
clientResults.push( clientResults.push(
/Hostname\/IP doesn't/.test(client.authorizationError || '')); /Hostname\/IP doesn't/.test(client.authorizationError || ''));
client.destroy(); client.destroy();
callback(); next();
}); });
};
connectClient(clientsOptions[0], function() { client.on('error', function(err) {
connectClient(clientsOptions[1], function() { clientResults.push(false);
connectClient(clientsOptions[2], function() { clientError = err.message;
server.close(); next();
});
}); });
function next() {
clientErrors.push(clientError);
serverErrors.push(serverError);
if (i === clientsOptions.length - 1)
callback();
else
connectClient(i + 1, callback);
}
};
connectClient(0, function() {
server.close();
}); });
} }
process.on('exit', function() { process.on('exit', function() {
assert.deepEqual(serverResults, ['a.example.com', 'b.example.com', assert.deepEqual(serverResults, ['a.example.com', 'b.example.com',
'c.wrong.com']); 'c.wrong.com', null]);
assert.deepEqual(clientResults, [true, true, false]); assert.deepEqual(clientResults, [true, true, false, false]);
assert.deepEqual(clientErrors, [null, null, null, "socket hang up"]);
assert.deepEqual(serverErrors, [null, null, null, "Invalid SNI context"]);
}); });

Loading…
Cancel
Save