• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24if (!common.hasCrypto)
25  common.skip('missing crypto');
26
27const assert = require('assert');
28const tls = require('tls');
29const fixtures = require('../common/fixtures');
30
31function loadPEM(n) {
32  return fixtures.readKey(`${n}.pem`);
33}
34
35const serverOptions = {
36  key: loadPEM('agent2-key'),
37  cert: loadPEM('agent2-cert'),
38  requestCert: true,
39  rejectUnauthorized: false,
40  SNICallback: function(servername, callback) {
41    const context = SNIContexts[servername];
42
43    // Just to test asynchronous callback
44    setTimeout(function() {
45      if (context) {
46        if (context.emptyRegression)
47          callback(null, {});
48        else
49          callback(null, tls.createSecureContext(context));
50      } else {
51        callback(null, null);
52      }
53    }, 100);
54  }
55};
56
57const SNIContexts = {
58  'a.example.com': {
59    key: loadPEM('agent1-key'),
60    cert: loadPEM('agent1-cert'),
61    ca: [ loadPEM('ca2-cert') ]
62  },
63  'b.example.com': {
64    key: loadPEM('agent3-key'),
65    cert: loadPEM('agent3-cert')
66  },
67  'c.another.com': {
68    emptyRegression: true
69  }
70};
71
72test({
73  port: undefined,
74  key: loadPEM('agent1-key'),
75  cert: loadPEM('agent1-cert'),
76  ca: [loadPEM('ca1-cert')],
77  servername: 'a.example.com',
78  rejectUnauthorized: false
79},
80     true,
81     { sni: 'a.example.com', authorized: false },
82     null,
83     null);
84
85test({
86  port: undefined,
87  key: loadPEM('agent4-key'),
88  cert: loadPEM('agent4-cert'),
89  ca: [loadPEM('ca1-cert')],
90  servername: 'a.example.com',
91  rejectUnauthorized: false
92},
93     true,
94     { sni: 'a.example.com', authorized: true },
95     null,
96     null);
97
98test({
99  port: undefined,
100  key: loadPEM('agent2-key'),
101  cert: loadPEM('agent2-cert'),
102  ca: [loadPEM('ca2-cert')],
103  servername: 'b.example.com',
104  rejectUnauthorized: false
105},
106     true,
107     { sni: 'b.example.com', authorized: false },
108     null,
109     null);
110
111test({
112  port: undefined,
113  key: loadPEM('agent3-key'),
114  cert: loadPEM('agent3-cert'),
115  ca: [loadPEM('ca1-cert')],
116  servername: 'c.wrong.com',
117  rejectUnauthorized: false
118},
119     false,
120     { sni: 'c.wrong.com', authorized: false },
121     null,
122     null);
123
124test({
125  port: undefined,
126  key: loadPEM('agent3-key'),
127  cert: loadPEM('agent3-cert'),
128  ca: [loadPEM('ca1-cert')],
129  servername: 'c.another.com',
130  rejectUnauthorized: false
131},
132     false,
133     null,
134     'Client network socket disconnected before secure TLS ' +
135       'connection was established',
136     'Invalid SNI context');
137
138function test(options, clientResult, serverResult, clientError, serverError) {
139  const server = tls.createServer(serverOptions, (c) => {
140    assert.deepStrictEqual(
141      serverResult,
142      { sni: c.servername, authorized: c.authorized }
143    );
144  });
145
146  if (serverResult) {
147    assert(!serverError);
148    server.on('tlsClientError', common.mustNotCall());
149  } else {
150    assert(serverError);
151    server.on('tlsClientError', common.mustCall((err) => {
152      assert.strictEqual(err.message, serverError);
153    }));
154  }
155
156  server.listen(0, () => {
157    options.port = server.address().port;
158    const client = tls.connect(options, () => {
159      const result = client.authorizationError &&
160        (client.authorizationError === 'ERR_TLS_CERT_ALTNAME_INVALID');
161      assert.strictEqual(result, clientResult);
162      client.end();
163    });
164
165    client.on('close', common.mustCall(() => server.close()));
166
167    if (clientError)
168      client.on('error', common.mustCall((err) => {
169        assert.strictEqual(err.message, clientError);
170      }));
171    else
172      client.on('error', common.mustNotCall());
173  });
174}
175