Skip to content

Commit

Permalink
tls_wrap: parse tls session ticket extension
Browse files Browse the repository at this point in the history
And, if present and non-empty, don't invoke `resumeSession` callback.

fix nodejs#5872
  • Loading branch information
indutny committed Aug 1, 2013
1 parent 30701d6 commit 4d78182
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
1 change: 1 addition & 0 deletions lib/_tls_wrap.js
Expand Up @@ -63,6 +63,7 @@ function onclienthello(hello) {
}

if (hello.sessionId.length <= 0 ||
hello.tlsTicket ||
this.server &&
!this.server.emit('resumeSession', hello.sessionId, callback)) {
callback(null, null);
Expand Down
66 changes: 66 additions & 0 deletions src/tls_wrap.cc
Expand Up @@ -30,6 +30,7 @@ namespace node {

using crypto::SecureContext;
using v8::Array;
using v8::Boolean;
using v8::Exception;
using v8::Function;
using v8::FunctionCallbackInfo;
Expand Down Expand Up @@ -63,6 +64,7 @@ static Cached<String> name_sym;
static Cached<String> version_sym;
static Cached<String> ext_key_usage_sym;
static Cached<String> sessionid_sym;
static Cached<String> tls_ticket_sym;

static Persistent<Function> tlsWrap;

Expand Down Expand Up @@ -682,6 +684,8 @@ void TLSCallbacks::ParseClientHello() {
bool is_clienthello = false;
uint8_t session_size = -1;
uint8_t* session_id = NULL;
uint16_t tls_ticket_size = -1;
uint8_t* tls_ticket = NULL;
Local<Object> hello_obj;
Handle<Value> argv[1];

Expand Down Expand Up @@ -739,6 +743,64 @@ void TLSCallbacks::ParseClientHello() {
session_size = *body;
session_id = body + 1;
}

size_t cipher_offset = session_offset + 1 + session_size;

// Session OOB failure
if (cipher_offset >= avail)
return ParseFinish();

uint16_t cipher_len = (data[cipher_offset] << 8) +
data[cipher_offset + 1];
size_t comp_offset = cipher_offset + 2 + cipher_len;

// Cipher OOB failure
if (comp_offset >= avail)
return ParseFinish();

uint8_t comp_len = data[comp_offset];
size_t extension_offset = comp_offset + 1 + comp_len;

// Compression OOB failure
if (extension_offset > avail)
return ParseFinish();

// Extensions present
if (extension_offset != avail) {
size_t ext_off = extension_offset + 2;

// Parse known extensions
while (ext_off < avail) {
// Extension OOB
if (avail - ext_off < 4)
return ParseFinish();

uint16_t ext_type = (data[ext_off] << 8) + data[ext_off + 1];
uint16_t ext_len = (data[ext_off + 2] << 8) + data[ext_off + 3];

// Extension OOB
if (ext_off + ext_len + 4 > avail)
return ParseFinish();

ext_off += 4;

// TLS Session Ticket
if (ext_type == 35) {
tls_ticket_size = ext_len;
tls_ticket = data + ext_off;

// Session Ticket OOB
if (tls_ticket_size + ext_off > avail)
return ParseFinish();
}

ext_off += ext_len;
}

// Extensions OOB failure
if (ext_off > avail)
return ParseFinish();
}
} else if (hello_.state == kParseSSLHeader) {
// Skip header, version
session_offset = hello_.body_offset + 3;
Expand Down Expand Up @@ -777,6 +839,9 @@ void TLSCallbacks::ParseClientHello() {
hello_obj->Set(sessionid_sym,
Buffer::New(reinterpret_cast<char*>(session_id),
session_size));
hello_obj->Set(tls_ticket_sym,
Boolean::New(tls_ticket != NULL &&
tls_ticket_size != 0));

argv[0] = hello_obj;
MakeCallback(object(), onclienthello_sym, 1, argv);
Expand Down Expand Up @@ -1364,6 +1429,7 @@ void TLSCallbacks::Initialize(Handle<Object> target) {
version_sym = String::New("version");
ext_key_usage_sym = String::New("ext_key_usage");
sessionid_sym = String::New("sessionId");
tls_ticket_sym = String::New("tlsTicket");
}

} // namespace node
Expand Down
34 changes: 24 additions & 10 deletions test/simple/test-tls-session-cache.js
Expand Up @@ -28,10 +28,14 @@ require('child_process').exec('openssl version', function(err) {
console.error('Skipping because openssl command is not available.');
process.exit(0);
}
doTest();
doTest({ tickets: false } , function() {
doTest({ tickets: true } , function() {
console.error('all done');
});
});
});

function doTest() {
function doTest(testOptions, callback) {
var common = require('../common');
var assert = require('assert');
var tls = require('tls');
Expand All @@ -50,6 +54,7 @@ function doTest() {
requestCert: true
};
var requestCount = 0;
var resumeCount = 0;
var session;
var badOpenSSL = false;

Expand All @@ -72,6 +77,7 @@ function doTest() {
};
});
server.on('resumeSession', function(id, callback) {
++resumeCount;
assert.ok(session);
assert.equal(session.id.toString('hex'), id.toString('hex'));

Expand All @@ -83,12 +89,12 @@ function doTest() {
server.listen(common.PORT, function() {
var client = spawn('openssl', [
's_client',
'-tls1',
'-connect', 'localhost:' + common.PORT,
'-key', join(common.fixturesDir, 'agent.key'),
'-cert', join(common.fixturesDir, 'agent.crt'),
'-reconnect',
'-no_ticket'
], {
'-reconnect'
].concat(testOptions.tickets ? [] : '-no_ticket'), {
stdio: [ 0, 1, 'pipe' ]
});
var err = '';
Expand All @@ -97,22 +103,30 @@ function doTest() {
err += chunk;
});
client.on('exit', function(code) {
console.error('done');
if (/^unknown option/.test(err)) {
// using an incompatible version of openssl
assert(code);
badOpenSSL = true;
} else
assert.equal(code, 0);
server.close();
server.close(function() {
setTimeout(callback, 100);
});
});
});

process.on('exit', function() {
if (!badOpenSSL) {
assert.ok(session);

// initial request + reconnect requests (5 times)
assert.equal(requestCount, 6);
if (testOptions.tickets) {
assert.equal(requestCount, 6);
assert.equal(resumeCount, 0);
} else {
// initial request + reconnect requests (5 times)
assert.ok(session);
assert.equal(requestCount, 6);
assert.equal(resumeCount, 5);
}
}
});
}

0 comments on commit 4d78182

Please sign in to comment.