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