Skip to content

Commit e3723ff

Browse files
mcollinaaduh95
authored andcommitted
test: add session reuse host verification regressions
Backport-PR-URL: nodejs-private/node-private#895 PR-URL: nodejs-private/node-private#854 Reviewed-By: Antoine du Hamel <[email protected]> Refs: https://hackerone.com/reports/3649802
1 parent a77af48 commit e3723ff

3 files changed

Lines changed: 394 additions & 0 deletions
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const fixtures = require('../common/fixtures');
8+
const h2 = require('http2');
9+
const tls = require('tls');
10+
const { once } = require('events');
11+
12+
function load(name) {
13+
return fixtures.readKey(name);
14+
}
15+
16+
const contexts = {
17+
agent1: tls.createSecureContext({
18+
key: load('agent1-key.pem'),
19+
cert: load('agent1-cert.pem'),
20+
}),
21+
agent3: tls.createSecureContext({
22+
key: load('agent3-key.pem'),
23+
cert: load('agent3-cert.pem'),
24+
}),
25+
};
26+
27+
function makeRequest(server, authority, options) {
28+
return new Promise((resolve, reject) => {
29+
let session;
30+
let settled = false;
31+
32+
function done(err, result) {
33+
if (settled)
34+
return;
35+
settled = true;
36+
37+
if (err) {
38+
client.destroy();
39+
reject(err);
40+
return;
41+
}
42+
43+
client.close();
44+
resolve({ session, result });
45+
}
46+
47+
const client = h2.connect(authority, {
48+
...options,
49+
ALPNProtocols: ['h2'],
50+
createConnection(url, connectOptions) {
51+
const socket = tls.connect({
52+
...connectOptions,
53+
ALPNProtocols: ['h2'],
54+
host: '127.0.0.1',
55+
port: server.address().port,
56+
servername: connectOptions.servername || url.hostname,
57+
});
58+
59+
socket.once('session', (value) => {
60+
if (session === undefined)
61+
session = value;
62+
});
63+
64+
return socket;
65+
},
66+
});
67+
68+
client.once('error', (err) => done(err));
69+
client.once('connect', () => {
70+
const req = client.request({ ':path': '/' });
71+
req.setEncoding('utf8');
72+
req.on('response', () => {});
73+
req.on('error', (err) => done(err));
74+
req.on('data', () => {});
75+
req.on('end', () => {
76+
done(null, {
77+
authority: client.socket.servername,
78+
authorized: client.socket.authorized,
79+
reused: client.socket.isSessionReused(),
80+
authorizationError: client.socket.authorizationError,
81+
});
82+
});
83+
req.end();
84+
});
85+
});
86+
}
87+
88+
const server = h2.createSecureServer({
89+
key: load('agent1-key.pem'),
90+
cert: load('agent1-cert.pem'),
91+
allowHTTP1: false,
92+
minVersion: 'TLSv1.2',
93+
maxVersion: 'TLSv1.2',
94+
SNICallback(servername, cb) {
95+
cb(null, contexts[servername] || contexts.agent1);
96+
},
97+
});
98+
99+
server.on('stream', (stream) => {
100+
stream.respond({ ':status': 200 });
101+
stream.end('ok');
102+
});
103+
104+
(async function() {
105+
server.listen(0);
106+
await once(server, 'listening');
107+
108+
try {
109+
const port = server.address().port;
110+
const first = await makeRequest(server, `https://agent1:${port}`, {
111+
ca: [load('ca1-cert.pem')],
112+
minVersion: 'TLSv1.2',
113+
maxVersion: 'TLSv1.2',
114+
});
115+
116+
assert.strictEqual(first.result.authorized, true);
117+
assert.strictEqual(first.result.reused, false);
118+
assert(first.session);
119+
120+
await assert.rejects(
121+
makeRequest(server, `https://agent3:${port}`, {
122+
ca: [load('ca1-cert.pem')],
123+
minVersion: 'TLSv1.2',
124+
maxVersion: 'TLSv1.2',
125+
session: first.session,
126+
}),
127+
{
128+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
129+
},
130+
);
131+
132+
await assert.rejects(
133+
makeRequest(server, `https://agent3:${port}`, {
134+
ca: [load('ca1-cert.pem')],
135+
minVersion: 'TLSv1.2',
136+
maxVersion: 'TLSv1.2',
137+
}),
138+
{
139+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
140+
},
141+
);
142+
} finally {
143+
server.close();
144+
await once(server, 'close');
145+
}
146+
})().then(common.mustCall());
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const fixtures = require('../common/fixtures');
8+
const https = require('https');
9+
const tls = require('tls');
10+
const { once } = require('events');
11+
12+
function load(name) {
13+
return fixtures.readKey(name);
14+
}
15+
16+
const contexts = {
17+
agent1: tls.createSecureContext({
18+
key: load('agent1-key.pem'),
19+
cert: load('agent1-cert.pem'),
20+
}),
21+
agent3: tls.createSecureContext({
22+
key: load('agent3-key.pem'),
23+
cert: load('agent3-cert.pem'),
24+
}),
25+
};
26+
27+
function request(options) {
28+
return new Promise((resolve, reject) => {
29+
const req = https.get({
30+
...options,
31+
host: '127.0.0.1',
32+
agent: false,
33+
}, (res) => {
34+
res.resume();
35+
res.once('end', () => resolve(res));
36+
});
37+
38+
req.once('error', reject);
39+
});
40+
}
41+
42+
async function requestAndCaptureSession(options) {
43+
let sessionResolve;
44+
const sessionPromise = new Promise((resolve) => {
45+
sessionResolve = resolve;
46+
});
47+
48+
const req = https.get({
49+
...options,
50+
host: '127.0.0.1',
51+
agent: false,
52+
});
53+
54+
req.on('socket', (socket) => {
55+
socket.once('session', sessionResolve);
56+
});
57+
58+
const res = await new Promise((resolve, reject) => {
59+
req.once('response', resolve);
60+
req.once('error', reject);
61+
});
62+
63+
assert.strictEqual(res.socket.authorized, true);
64+
assert.strictEqual(res.socket.isSessionReused(), false);
65+
66+
res.resume();
67+
await once(res, 'end');
68+
69+
const session = await sessionPromise;
70+
assert(session);
71+
return session;
72+
}
73+
74+
const server = https.createServer({
75+
key: load('agent1-key.pem'),
76+
cert: load('agent1-cert.pem'),
77+
minVersion: 'TLSv1.2',
78+
maxVersion: 'TLSv1.2',
79+
SNICallback(servername, cb) {
80+
cb(null, contexts[servername] || contexts.agent1);
81+
},
82+
}, (req, res) => {
83+
res.end('ok');
84+
});
85+
86+
(async function() {
87+
server.listen(0);
88+
await once(server, 'listening');
89+
90+
try {
91+
const session = await requestAndCaptureSession({
92+
port: server.address().port,
93+
servername: 'agent1',
94+
rejectUnauthorized: true,
95+
ca: [load('ca1-cert.pem')],
96+
});
97+
98+
await assert.rejects(
99+
request({
100+
port: server.address().port,
101+
servername: 'agent3',
102+
session,
103+
rejectUnauthorized: true,
104+
ca: [load('ca1-cert.pem')],
105+
}),
106+
{
107+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
108+
},
109+
);
110+
111+
await assert.rejects(
112+
request({
113+
port: server.address().port,
114+
servername: 'agent3',
115+
rejectUnauthorized: true,
116+
ca: [load('ca1-cert.pem')],
117+
}),
118+
{
119+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
120+
},
121+
);
122+
} finally {
123+
server.close();
124+
await once(server, 'close');
125+
}
126+
})().then(common.mustCall());
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const fixtures = require('../common/fixtures');
8+
const tls = require('tls');
9+
const { once } = require('events');
10+
11+
function load(name) {
12+
return fixtures.readKey(name);
13+
}
14+
15+
const contexts = {
16+
agent1: tls.createSecureContext({
17+
key: load('agent1-key.pem'),
18+
cert: load('agent1-cert.pem'),
19+
}),
20+
agent3: tls.createSecureContext({
21+
key: load('agent3-key.pem'),
22+
cert: load('agent3-cert.pem'),
23+
}),
24+
};
25+
26+
function connect(options) {
27+
return new Promise((resolve, reject) => {
28+
const socket = tls.connect(options);
29+
socket.once('secureConnect', () => resolve(socket));
30+
socket.once('error', reject);
31+
socket.resume();
32+
});
33+
}
34+
35+
async function connectAndCaptureSession(options) {
36+
const socket = tls.connect(options);
37+
const sessionPromise = once(socket, 'session').then(([session]) => session);
38+
39+
socket.resume();
40+
await once(socket, 'secureConnect');
41+
42+
assert.strictEqual(socket.authorized, true);
43+
assert.strictEqual(socket.isSessionReused(), false);
44+
45+
const session = await sessionPromise;
46+
assert(session);
47+
48+
await once(socket, 'close');
49+
return session;
50+
}
51+
52+
async function testVersion(minVersion, maxVersion) {
53+
const server = tls.createServer({
54+
key: load('agent1-key.pem'),
55+
cert: load('agent1-cert.pem'),
56+
minVersion,
57+
maxVersion,
58+
SNICallback(servername, cb) {
59+
cb(null, contexts[servername] || contexts.agent1);
60+
},
61+
}, (socket) => {
62+
socket.end('ok');
63+
});
64+
65+
server.listen(0);
66+
await once(server, 'listening');
67+
68+
try {
69+
const port = server.address().port;
70+
const session = await connectAndCaptureSession({
71+
port,
72+
host: '127.0.0.1',
73+
servername: 'agent1',
74+
minVersion,
75+
maxVersion,
76+
ca: [load('ca1-cert.pem')],
77+
});
78+
79+
await assert.rejects(
80+
connect({
81+
port,
82+
host: '127.0.0.1',
83+
servername: 'agent3',
84+
session,
85+
minVersion,
86+
maxVersion,
87+
ca: [load('ca1-cert.pem')],
88+
}).then((socket) => {
89+
socket.destroy();
90+
return socket;
91+
}),
92+
{
93+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
94+
},
95+
);
96+
97+
await assert.rejects(
98+
connect({
99+
port,
100+
host: '127.0.0.1',
101+
servername: 'agent3',
102+
minVersion,
103+
maxVersion,
104+
ca: [load('ca1-cert.pem')],
105+
}).then((socket) => {
106+
socket.destroy();
107+
return socket;
108+
}),
109+
{
110+
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
111+
},
112+
);
113+
} finally {
114+
server.close();
115+
await once(server, 'close');
116+
}
117+
}
118+
119+
(async function() {
120+
await testVersion('TLSv1.2', 'TLSv1.2');
121+
await testVersion('TLSv1.3', 'TLSv1.3');
122+
})().then(common.mustCall());

0 commit comments

Comments
 (0)