Remove excessive copyright/license boilerplate
[platform/upstream/nodejs.git] / test / parallel / test-tls-server-verify.js
1 var common = require('../common');
2
3 if (!common.opensslCli) {
4   console.error('Skipping because node compiled without OpenSSL CLI.');
5   process.exit(0);
6 }
7
8 // This is a rather complex test which sets up various TLS servers with node
9 // and connects to them using the 'openssl s_client' command line utility
10 // with various keys. Depending on the certificate authority and other
11 // parameters given to the server, the various clients are
12 // - rejected,
13 // - accepted and "unauthorized", or
14 // - accepted and "authorized".
15
16 var testCases =
17     [{ title: 'Do not request certs. Everyone is unauthorized.',
18       requestCert: false,
19       rejectUnauthorized: false,
20       renegotiate: false,
21       CAs: ['ca1-cert'],
22       clients:
23        [{ name: 'agent1', shouldReject: false, shouldAuth: false },
24         { name: 'agent2', shouldReject: false, shouldAuth: false },
25         { name: 'agent3', shouldReject: false, shouldAuth: false },
26         { name: 'nocert', shouldReject: false, shouldAuth: false }
27        ]
28     },
29
30     { title: 'Allow both authed and unauthed connections with CA1',
31       requestCert: true,
32       rejectUnauthorized: false,
33       renegotiate: false,
34       CAs: ['ca1-cert'],
35       clients:
36        [{ name: 'agent1', shouldReject: false, shouldAuth: true },
37         { name: 'agent2', shouldReject: false, shouldAuth: false },
38         { name: 'agent3', shouldReject: false, shouldAuth: false },
39         { name: 'nocert', shouldReject: false, shouldAuth: false }
40        ]
41     },
42
43     { title: 'Do not request certs at connection. Do that later',
44       requestCert: false,
45       rejectUnauthorized: false,
46       renegotiate: true,
47       CAs: ['ca1-cert'],
48       clients:
49        [{ name: 'agent1', shouldReject: false, shouldAuth: true },
50         { name: 'agent2', shouldReject: false, shouldAuth: false },
51         { name: 'agent3', shouldReject: false, shouldAuth: false },
52         { name: 'nocert', shouldReject: false, shouldAuth: false }
53        ]
54     },
55
56     { title: 'Allow only authed connections with CA1',
57       requestCert: true,
58       rejectUnauthorized: true,
59       renegotiate: false,
60       CAs: ['ca1-cert'],
61       clients:
62        [{ name: 'agent1', shouldReject: false, shouldAuth: true },
63         { name: 'agent2', shouldReject: true },
64         { name: 'agent3', shouldReject: true },
65         { name: 'nocert', shouldReject: true }
66        ]
67     },
68
69     { title: 'Allow only authed connections with CA1 and CA2',
70       requestCert: true,
71       rejectUnauthorized: true,
72       renegotiate: false,
73       CAs: ['ca1-cert', 'ca2-cert'],
74       clients:
75        [{ name: 'agent1', shouldReject: false, shouldAuth: true },
76         { name: 'agent2', shouldReject: true },
77         { name: 'agent3', shouldReject: false, shouldAuth: true },
78         { name: 'nocert', shouldReject: true }
79        ]
80     },
81
82
83     { title: 'Allow only certs signed by CA2 but not in the CRL',
84       requestCert: true,
85       rejectUnauthorized: true,
86       renegotiate: false,
87       CAs: ['ca2-cert'],
88       crl: 'ca2-crl',
89       clients:
90        [
91         { name: 'agent1', shouldReject: true, shouldAuth: false },
92         { name: 'agent2', shouldReject: true, shouldAuth: false },
93         { name: 'agent3', shouldReject: false, shouldAuth: true },
94         // Agent4 has a cert in the CRL.
95         { name: 'agent4', shouldReject: true, shouldAuth: false },
96         { name: 'nocert', shouldReject: true }
97        ]
98     }
99     ];
100
101
102 var constants = require('constants');
103 var assert = require('assert');
104 var fs = require('fs');
105 var tls = require('tls');
106 var spawn = require('child_process').spawn;
107
108
109 function filenamePEM(n) {
110   return require('path').join(common.fixturesDir, 'keys', n + '.pem');
111 }
112
113
114 function loadPEM(n) {
115   return fs.readFileSync(filenamePEM(n));
116 }
117
118
119 var serverKey = loadPEM('agent2-key');
120 var serverCert = loadPEM('agent2-cert');
121
122
123 function runClient(options, cb) {
124
125   // Client can connect in three ways:
126   // - Self-signed cert
127   // - Certificate, but not signed by CA.
128   // - Certificate signed by CA.
129
130   var args = ['s_client', '-connect', '127.0.0.1:' + common.PORT];
131
132
133   console.log('  connecting with', options.name);
134
135   switch (options.name) {
136     case 'agent1':
137       // Signed by CA1
138       args.push('-key');
139       args.push(filenamePEM('agent1-key'));
140       args.push('-cert');
141       args.push(filenamePEM('agent1-cert'));
142       break;
143
144     case 'agent2':
145       // Self-signed
146       // This is also the key-cert pair that the server will use.
147       args.push('-key');
148       args.push(filenamePEM('agent2-key'));
149       args.push('-cert');
150       args.push(filenamePEM('agent2-cert'));
151       break;
152
153     case 'agent3':
154       // Signed by CA2
155       args.push('-key');
156       args.push(filenamePEM('agent3-key'));
157       args.push('-cert');
158       args.push(filenamePEM('agent3-cert'));
159       break;
160
161     case 'agent4':
162       // Signed by CA2 (rejected by ca2-crl)
163       args.push('-key');
164       args.push(filenamePEM('agent4-key'));
165       args.push('-cert');
166       args.push(filenamePEM('agent4-cert'));
167       break;
168
169     case 'nocert':
170       // Do not send certificate
171       break;
172
173     default:
174       throw new Error('Unknown agent name');
175   }
176
177   // To test use: openssl s_client -connect localhost:8000
178   var client = spawn(common.opensslCli, args);
179
180   var out = '';
181
182   var rejected = true;
183   var authed = false;
184   var goodbye = false;
185
186   client.stdout.setEncoding('utf8');
187   client.stdout.on('data', function(d) {
188     out += d;
189
190     if (!goodbye && /_unauthed/g.test(out)) {
191       console.error('  * unauthed');
192       goodbye = true;
193       client.stdin.end('goodbye\n');
194       authed = false;
195       rejected = false;
196     }
197
198     if (!goodbye && /_authed/g.test(out)) {
199       console.error('  * authed');
200       goodbye = true;
201       client.stdin.end('goodbye\n');
202       authed = true;
203       rejected = false;
204     }
205   });
206
207   //client.stdout.pipe(process.stdout);
208
209   client.on('exit', function(code) {
210     //assert.equal(0, code, options.name +
211     //      ": s_client exited with error code " + code);
212     if (options.shouldReject) {
213       assert.equal(true, rejected, options.name +
214           ' NOT rejected, but should have been');
215     } else {
216       assert.equal(false, rejected, options.name +
217           ' rejected, but should NOT have been');
218       assert.equal(options.shouldAuth, authed);
219     }
220
221     cb();
222   });
223 }
224
225
226 // Run the tests
227 var successfulTests = 0;
228 function runTest(testIndex) {
229   var tcase = testCases[testIndex];
230   if (!tcase) return;
231
232   console.error("Running '%s'", tcase.title);
233
234   var cas = tcase.CAs.map(loadPEM);
235
236   var crl = tcase.crl ? loadPEM(tcase.crl) : null;
237
238   var serverOptions = {
239     key: serverKey,
240     cert: serverCert,
241     ca: cas,
242     crl: crl,
243     requestCert: tcase.requestCert,
244     rejectUnauthorized: tcase.rejectUnauthorized
245   };
246
247   var connections = 0;
248
249   /*
250    * If renegotiating - session might be resumed and openssl won't request
251    * client's certificate (probably because of bug in the openssl)
252    */
253   if (tcase.renegotiate) {
254     serverOptions.secureOptions =
255         constants.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
256   }
257
258   var renegotiated = false;
259   var server = tls.Server(serverOptions, function handleConnection(c) {
260     if (tcase.renegotiate && !renegotiated) {
261       renegotiated = true;
262       setTimeout(function() {
263         console.error('- connected, renegotiating');
264         c.write('\n_renegotiating\n');
265         return c.renegotiate({
266           requestCert: true,
267           rejectUnauthorized: false
268         }, function(err) {
269           assert(!err);
270           c.write('\n_renegotiated\n');
271           handleConnection(c);
272         });
273       }, 200);
274       return;
275     }
276
277     connections++;
278     if (c.authorized) {
279       console.error('- authed connection: ' +
280                     c.getPeerCertificate().subject.CN);
281       c.write('\n_authed\n');
282     } else {
283       console.error('- unauthed connection: %s', c.authorizationError);
284       c.write('\n_unauthed\n');
285     }
286   });
287
288   function runNextClient(clientIndex) {
289     var options = tcase.clients[clientIndex];
290     if (options) {
291       runClient(options, function() {
292         runNextClient(clientIndex + 1);
293       });
294     } else {
295       server.close();
296       successfulTests++;
297       runTest(testIndex + 1);
298     }
299   }
300
301   server.listen(common.PORT, function() {
302     if (tcase.debug) {
303       console.error('TLS server running on port ' + common.PORT);
304     } else {
305       runNextClient(0);
306     }
307   });
308 }
309
310
311 runTest(0);
312
313
314 process.on('exit', function() {
315   assert.equal(successfulTests, testCases.length);
316 });