1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "config.h"
7 
8 #include <algorithm>
9 #include <cstdlib>
10 #include <iostream>
11 #include <memory>
12 #include "nspr.h"
13 #include "nss.h"
14 #include "prio.h"
15 #include "prnetdb.h"
16 #include "secerr.h"
17 #include "ssl.h"
18 #include "ssl3prot.h"
19 #include "sslerr.h"
20 #include "sslproto.h"
21 #include "nss_scoped_ptrs.h"
22 
23 #include "nsskeys.h"
24 
25 static const char* kVersionDisableFlags[] = {"no-ssl3", "no-tls1", "no-tls11",
26                                              "no-tls12", "no-tls13"};
27 
28 bool exitCodeUnimplemented = false;
29 
FormatError(PRErrorCode code)30 std::string FormatError(PRErrorCode code) {
31   return std::string(":") + PORT_ErrorToName(code) + ":" + ":" +
32          PORT_ErrorToString(code);
33 }
34 
35 class TestAgent {
36  public:
TestAgent(const Config & cfg)37   TestAgent(const Config& cfg) : cfg_(cfg) {}
38 
~TestAgent()39   ~TestAgent() {}
40 
Create(const Config & cfg)41   static std::unique_ptr<TestAgent> Create(const Config& cfg) {
42     std::unique_ptr<TestAgent> agent(new TestAgent(cfg));
43 
44     if (!agent->Init()) return nullptr;
45 
46     return agent;
47   }
48 
Init()49   bool Init() {
50     if (!ConnectTcp()) {
51       return false;
52     }
53 
54     if (!SetupKeys()) {
55       std::cerr << "Couldn't set up keys/certs\n";
56       return false;
57     }
58 
59     if (!SetupOptions()) {
60       std::cerr << "Couldn't configure socket\n";
61       return false;
62     }
63 
64     SECStatus rv = SSL_ResetHandshake(ssl_fd_.get(), cfg_.get<bool>("server"));
65     if (rv != SECSuccess) return false;
66 
67     return true;
68   }
69 
ConnectTcp()70   bool ConnectTcp() {
71     // Try IPv6 first, then IPv4 in case of failure.
72     if (!OpenConnection("::1") && !OpenConnection("127.0.0.1")) {
73       return false;
74     }
75 
76     ssl_fd_ = ScopedPRFileDesc(SSL_ImportFD(NULL, pr_fd_.get()));
77     if (!ssl_fd_) {
78       return false;
79     }
80     pr_fd_.release();
81 
82     return true;
83   }
84 
OpenConnection(const char * ip)85   bool OpenConnection(const char* ip) {
86     PRStatus prv;
87     PRNetAddr addr;
88 
89     prv = PR_StringToNetAddr(ip, &addr);
90 
91     if (prv != PR_SUCCESS) {
92       return false;
93     }
94 
95     addr.inet.port = PR_htons(cfg_.get<int>("port"));
96 
97     pr_fd_ = ScopedPRFileDesc(PR_OpenTCPSocket(addr.raw.family));
98     if (!pr_fd_) return false;
99 
100     prv = PR_Connect(pr_fd_.get(), &addr, PR_INTERVAL_NO_TIMEOUT);
101     if (prv != PR_SUCCESS) {
102       return false;
103     }
104     return true;
105   }
106 
SetupKeys()107   bool SetupKeys() {
108     SECStatus rv;
109 
110     if (cfg_.get<std::string>("key-file") != "") {
111       key_ = ScopedSECKEYPrivateKey(
112           ReadPrivateKey(cfg_.get<std::string>("key-file")));
113       if (!key_) return false;
114     }
115     if (cfg_.get<std::string>("cert-file") != "") {
116       cert_ = ScopedCERTCertificate(
117           ReadCertificate(cfg_.get<std::string>("cert-file")));
118       if (!cert_) return false;
119     }
120 
121     // Needed because certs are not entirely valid.
122     rv = SSL_AuthCertificateHook(ssl_fd_.get(), AuthCertificateHook, this);
123     if (rv != SECSuccess) return false;
124 
125     if (cfg_.get<bool>("server")) {
126       // Server
127       rv = SSL_ConfigServerCert(ssl_fd_.get(), cert_.get(), key_.get(), nullptr,
128                                 0);
129       if (rv != SECSuccess) {
130         std::cerr << "Couldn't configure server cert\n";
131         return false;
132       }
133 
134     } else if (key_ && cert_) {
135       // Client.
136       rv =
137           SSL_GetClientAuthDataHook(ssl_fd_.get(), GetClientAuthDataHook, this);
138       if (rv != SECSuccess) return false;
139     }
140 
141     return true;
142   }
143 
ConvertFromWireVersion(SSLProtocolVariant variant,int wire_version,uint16_t * lib_version)144   static bool ConvertFromWireVersion(SSLProtocolVariant variant,
145                                      int wire_version, uint16_t* lib_version) {
146     // These default values are used when {min,max}-version isn't given.
147     if (wire_version == 0 || wire_version == 0xffff) {
148       *lib_version = static_cast<uint16_t>(wire_version);
149       return true;
150     }
151 
152 #ifdef TLS_1_3_DRAFT_VERSION
153     if (wire_version == (0x7f00 | TLS_1_3_DRAFT_VERSION)) {
154       // N.B. SSL_LIBRARY_VERSION_DTLS_1_3_WIRE == SSL_LIBRARY_VERSION_TLS_1_3
155       wire_version = SSL_LIBRARY_VERSION_TLS_1_3;
156     }
157 #endif
158 
159     if (variant == ssl_variant_datagram) {
160       switch (wire_version) {
161         case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
162           *lib_version = SSL_LIBRARY_VERSION_DTLS_1_0;
163           break;
164         case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
165           *lib_version = SSL_LIBRARY_VERSION_DTLS_1_2;
166           break;
167         case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
168           *lib_version = SSL_LIBRARY_VERSION_DTLS_1_3;
169           break;
170         default:
171           std::cerr << "Unrecognized DTLS version " << wire_version << ".\n";
172           return false;
173       }
174     } else {
175       if (wire_version < SSL_LIBRARY_VERSION_3_0 ||
176           wire_version > SSL_LIBRARY_VERSION_TLS_1_3) {
177         std::cerr << "Unrecognized TLS version " << wire_version << ".\n";
178         return false;
179       }
180       *lib_version = static_cast<uint16_t>(wire_version);
181     }
182     return true;
183   }
184 
GetVersionRange(SSLVersionRange * range_out,SSLProtocolVariant variant)185   bool GetVersionRange(SSLVersionRange* range_out, SSLProtocolVariant variant) {
186     SSLVersionRange supported;
187     if (SSL_VersionRangeGetSupported(variant, &supported) != SECSuccess) {
188       return false;
189     }
190 
191     uint16_t min_allowed;
192     uint16_t max_allowed;
193     if (!ConvertFromWireVersion(variant, cfg_.get<int>("min-version"),
194                                 &min_allowed)) {
195       return false;
196     }
197     if (!ConvertFromWireVersion(variant, cfg_.get<int>("max-version"),
198                                 &max_allowed)) {
199       return false;
200     }
201 
202     min_allowed = std::max(min_allowed, supported.min);
203     max_allowed = std::min(max_allowed, supported.max);
204 
205     bool found_min = false;
206     bool found_max = false;
207     // Ignore -no-ssl3, because SSLv3 is never supported.
208     for (size_t i = 1; i < PR_ARRAY_SIZE(kVersionDisableFlags); ++i) {
209       auto version =
210           static_cast<uint16_t>(SSL_LIBRARY_VERSION_TLS_1_0 + (i - 1));
211       if (variant == ssl_variant_datagram) {
212         // In DTLS mode, the -no-tlsN flags refer to DTLS versions,
213         // but NSS wants the corresponding TLS versions.
214         if (version == SSL_LIBRARY_VERSION_TLS_1_1) {
215           // DTLS 1.1 doesn't exist.
216           continue;
217         }
218         if (version == SSL_LIBRARY_VERSION_TLS_1_0) {
219           version = SSL_LIBRARY_VERSION_DTLS_1_0;
220         }
221       }
222 
223       if (version < min_allowed) {
224         continue;
225       }
226       if (version > max_allowed) {
227         break;
228       }
229 
230       const bool allowed = !cfg_.get<bool>(kVersionDisableFlags[i]);
231 
232       if (!found_min && allowed) {
233         found_min = true;
234         range_out->min = version;
235       }
236       if (found_min && !found_max) {
237         if (allowed) {
238           range_out->max = version;
239         } else {
240           found_max = true;
241         }
242       }
243       if (found_max && allowed) {
244         std::cerr << "Discontiguous version range.\n";
245         return false;
246       }
247     }
248 
249     if (!found_min) {
250       std::cerr << "All versions disabled.\n";
251     }
252     return found_min;
253   }
254 
SetupOptions()255   bool SetupOptions() {
256     SECStatus rv =
257         SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE);
258     if (rv != SECSuccess) return false;
259 
260     rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
261     if (rv != SECSuccess) return false;
262 
263     SSLVersionRange vrange;
264     if (!GetVersionRange(&vrange, ssl_variant_stream)) return false;
265 
266     rv = SSL_VersionRangeSet(ssl_fd_.get(), &vrange);
267     if (rv != SECSuccess) return false;
268 
269     SSLVersionRange verify_vrange;
270     rv = SSL_VersionRangeGet(ssl_fd_.get(), &verify_vrange);
271     if (rv != SECSuccess) return false;
272     if (vrange.min != verify_vrange.min || vrange.max != verify_vrange.max)
273       return false;
274 
275     rv = SSL_OptionSet(ssl_fd_.get(), SSL_NO_CACHE, false);
276     if (rv != SECSuccess) return false;
277 
278     auto alpn = cfg_.get<std::string>("advertise-alpn");
279     if (!alpn.empty()) {
280       assert(!cfg_.get<bool>("server"));
281 
282       rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_ALPN, PR_TRUE);
283       if (rv != SECSuccess) return false;
284 
285       rv = SSL_SetNextProtoNego(
286           ssl_fd_.get(), reinterpret_cast<const unsigned char*>(alpn.c_str()),
287           alpn.size());
288       if (rv != SECSuccess) return false;
289     }
290 
291     // Set supported signature schemes.
292     auto sign_prefs = cfg_.get<std::vector<int>>("signing-prefs");
293     auto verify_prefs = cfg_.get<std::vector<int>>("verify-prefs");
294     if (sign_prefs.empty()) {
295       sign_prefs = verify_prefs;
296     } else if (!verify_prefs.empty()) {
297       return false;  // Both shouldn't be set.
298     }
299     if (!sign_prefs.empty()) {
300       std::vector<SSLSignatureScheme> sig_schemes;
301       std::transform(
302           sign_prefs.begin(), sign_prefs.end(), std::back_inserter(sig_schemes),
303           [](int scheme) { return static_cast<SSLSignatureScheme>(scheme); });
304 
305       rv = SSL_SignatureSchemePrefSet(
306           ssl_fd_.get(), sig_schemes.data(),
307           static_cast<unsigned int>(sig_schemes.size()));
308       if (rv != SECSuccess) return false;
309     }
310 
311     if (cfg_.get<bool>("fallback-scsv")) {
312       rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
313       if (rv != SECSuccess) return false;
314     }
315 
316     if (cfg_.get<bool>("false-start")) {
317       rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_FALSE_START, PR_TRUE);
318       if (rv != SECSuccess) return false;
319     }
320 
321     if (cfg_.get<bool>("enable-ocsp-stapling")) {
322       rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
323       if (rv != SECSuccess) return false;
324     }
325 
326     bool requireClientCert = cfg_.get<bool>("require-any-client-certificate");
327     if (requireClientCert || cfg_.get<bool>("verify-peer")) {
328       assert(cfg_.get<bool>("server"));
329 
330       rv = SSL_OptionSet(ssl_fd_.get(), SSL_REQUEST_CERTIFICATE, PR_TRUE);
331       if (rv != SECSuccess) return false;
332 
333       rv = SSL_OptionSet(
334           ssl_fd_.get(), SSL_REQUIRE_CERTIFICATE,
335           requireClientCert ? SSL_REQUIRE_ALWAYS : SSL_REQUIRE_NO_ERROR);
336       if (rv != SECSuccess) return false;
337     }
338 
339     if (!cfg_.get<bool>("server")) {
340       // Needed to make resumption work.
341       rv = SSL_SetURL(ssl_fd_.get(), "server");
342       if (rv != SECSuccess) return false;
343     }
344 
345     rv = SSL_OptionSet(ssl_fd_.get(), SSL_ENABLE_EXTENDED_MASTER_SECRET,
346                        PR_TRUE);
347     if (rv != SECSuccess) return false;
348 
349     if (!ConfigureCiphers()) return false;
350 
351     return true;
352   }
353 
ConfigureCiphers()354   bool ConfigureCiphers() {
355     auto cipherList = cfg_.get<std::string>("nss-cipher");
356 
357     if (cipherList.empty()) {
358       return EnableNonExportCiphers();
359     }
360 
361     for (size_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
362       SSLCipherSuiteInfo csinfo;
363       std::string::size_type n;
364       SECStatus rv = SSL_GetCipherSuiteInfo(SSL_ImplementedCiphers[i], &csinfo,
365                                             sizeof(csinfo));
366       if (rv != SECSuccess) {
367         return false;
368       }
369 
370       // Check if cipherList contains the name of the Cipher Suite and
371       // enable/disable accordingly.
372       n = cipherList.find(csinfo.cipherSuiteName, 0);
373       if (std::string::npos == n) {
374         rv = SSL_CipherPrefSet(ssl_fd_.get(), SSL_ImplementedCiphers[i],
375                                PR_FALSE);
376       } else {
377         rv = SSL_CipherPrefSet(ssl_fd_.get(), SSL_ImplementedCiphers[i],
378                                PR_TRUE);
379       }
380       if (rv != SECSuccess) {
381         return false;
382       }
383     }
384     return true;
385   }
386 
EnableNonExportCiphers()387   bool EnableNonExportCiphers() {
388     for (size_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
389       SSLCipherSuiteInfo csinfo;
390 
391       SECStatus rv = SSL_GetCipherSuiteInfo(SSL_ImplementedCiphers[i], &csinfo,
392                                             sizeof(csinfo));
393       if (rv != SECSuccess) {
394         return false;
395       }
396 
397       rv = SSL_CipherPrefSet(ssl_fd_.get(), SSL_ImplementedCiphers[i], PR_TRUE);
398       if (rv != SECSuccess) {
399         return false;
400       }
401     }
402     return true;
403   }
404 
405   // Dummy auth certificate hook.
AuthCertificateHook(void * arg,PRFileDesc * fd,PRBool checksig,PRBool isServer)406   static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
407                                        PRBool checksig, PRBool isServer) {
408     return SECSuccess;
409   }
410 
GetClientAuthDataHook(void * self,PRFileDesc * fd,CERTDistNames * caNames,CERTCertificate ** cert,SECKEYPrivateKey ** privKey)411   static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
412                                          CERTDistNames* caNames,
413                                          CERTCertificate** cert,
414                                          SECKEYPrivateKey** privKey) {
415     TestAgent* a = static_cast<TestAgent*>(self);
416     *cert = CERT_DupCertificate(a->cert_.get());
417     *privKey = SECKEY_CopyPrivateKey(a->key_.get());
418     return SECSuccess;
419   }
420 
Handshake()421   SECStatus Handshake() { return SSL_ForceHandshake(ssl_fd_.get()); }
422 
423   // Implement a trivial echo client/server. Read bytes from the other side,
424   // flip all the bits, and send them back.
ReadWrite()425   SECStatus ReadWrite() {
426     for (;;) {
427       uint8_t block[512];
428       int32_t rv = PR_Read(ssl_fd_.get(), block, sizeof(block));
429       if (rv < 0) {
430         std::cerr << "Failure reading\n";
431         return SECFailure;
432       }
433       if (rv == 0) return SECSuccess;
434 
435       int32_t len = rv;
436       for (int32_t i = 0; i < len; ++i) {
437         block[i] ^= 0xff;
438       }
439 
440       rv = PR_Write(ssl_fd_.get(), block, len);
441       if (rv != len) {
442         std::cerr << "Write failure\n";
443         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
444         return SECFailure;
445       }
446     }
447     return SECSuccess;
448   }
449 
450   // Write bytes to the other side then read them back and check
451   // that they were correctly XORed as in ReadWrite.
WriteRead()452   SECStatus WriteRead() {
453     static const uint8_t ch = 'E';
454 
455     // We do 600-byte blocks to provide mis-alignment of the
456     // reader and writer.
457     uint8_t block[600];
458     memset(block, ch, sizeof(block));
459     int32_t rv = PR_Write(ssl_fd_.get(), block, sizeof(block));
460     if (rv != sizeof(block)) {
461       std::cerr << "Write failure\n";
462       PORT_SetError(SEC_ERROR_OUTPUT_LEN);
463       return SECFailure;
464     }
465 
466     size_t left = sizeof(block);
467     while (left) {
468       rv = PR_Read(ssl_fd_.get(), block, left);
469       if (rv < 0) {
470         std::cerr << "Failure reading\n";
471         return SECFailure;
472       }
473       if (rv == 0) {
474         PORT_SetError(SEC_ERROR_INPUT_LEN);
475         return SECFailure;
476       }
477 
478       int32_t len = rv;
479       for (int32_t i = 0; i < len; ++i) {
480         if (block[i] != (ch ^ 0xff)) {
481           PORT_SetError(SEC_ERROR_BAD_DATA);
482           return SECFailure;
483         }
484       }
485       left -= len;
486     }
487     return SECSuccess;
488   }
489 
DoExchange()490   SECStatus DoExchange() {
491     SECStatus rv = Handshake();
492     if (rv != SECSuccess) {
493       PRErrorCode err = PR_GetError();
494       std::cerr << "Handshake failed with error=" << err << FormatError(err)
495                 << std::endl;
496       return SECFailure;
497     }
498 
499     if (cfg_.get<bool>("write-then-read")) {
500       rv = WriteRead();
501       if (rv != SECSuccess) {
502         PRErrorCode err = PR_GetError();
503         std::cerr << "WriteRead failed with error=" << FormatError(err)
504                   << std::endl;
505         return SECFailure;
506       }
507     } else {
508       rv = ReadWrite();
509       if (rv != SECSuccess) {
510         PRErrorCode err = PR_GetError();
511         std::cerr << "ReadWrite failed with error=" << FormatError(err)
512                   << std::endl;
513         return SECFailure;
514       }
515     }
516 
517     auto alpn = cfg_.get<std::string>("expect-alpn");
518     if (!alpn.empty()) {
519       SSLNextProtoState state;
520       char chosen[256];
521       unsigned int chosen_len;
522       rv = SSL_GetNextProto(ssl_fd_.get(), &state,
523                             reinterpret_cast<unsigned char*>(chosen),
524                             &chosen_len, sizeof(chosen));
525       if (rv != SECSuccess) {
526         PRErrorCode err = PR_GetError();
527         std::cerr << "SSL_GetNextProto failed with error=" << FormatError(err)
528                   << std::endl;
529         return SECFailure;
530       }
531 
532       assert(chosen_len <= sizeof(chosen));
533       if (std::string(chosen, chosen_len) != alpn) {
534         std::cerr << "Unexpected ALPN selection" << std::endl;
535         return SECFailure;
536       }
537     }
538 
539     auto sig_alg = cfg_.get<int>("expect-peer-signature-algorithm");
540     if (sig_alg) {
541       SSLChannelInfo info;
542       rv = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info));
543       if (rv != SECSuccess) {
544         PRErrorCode err = PR_GetError();
545         std::cerr << "SSL_GetChannelInfo failed with error=" << FormatError(err)
546                   << std::endl;
547         return SECFailure;
548       }
549 
550       auto expected = static_cast<SSLSignatureScheme>(sig_alg);
551       if (info.signatureScheme != expected) {
552         std::cerr << "Unexpected signature scheme" << std::endl;
553         return SECFailure;
554       }
555     }
556 
557     return SECSuccess;
558   }
559 
560  private:
561   const Config& cfg_;
562   ScopedPRFileDesc pr_fd_;
563   ScopedPRFileDesc ssl_fd_;
564   ScopedCERTCertificate cert_;
565   ScopedSECKEYPrivateKey key_;
566 };
567 
ReadConfig(int argc,char ** argv)568 std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
569   std::unique_ptr<Config> cfg(new Config());
570 
571   cfg->AddEntry<int>("port", 0);
572   cfg->AddEntry<bool>("server", false);
573   cfg->AddEntry<int>("resume-count", 0);
574   cfg->AddEntry<std::string>("key-file", "");
575   cfg->AddEntry<std::string>("cert-file", "");
576   cfg->AddEntry<int>("min-version", 0);
577   cfg->AddEntry<int>("max-version", 0xffff);
578   for (auto flag : kVersionDisableFlags) {
579     cfg->AddEntry<bool>(flag, false);
580   }
581   cfg->AddEntry<bool>("fallback-scsv", false);
582   cfg->AddEntry<bool>("false-start", false);
583   cfg->AddEntry<bool>("enable-ocsp-stapling", false);
584   cfg->AddEntry<bool>("write-then-read", false);
585   cfg->AddEntry<bool>("require-any-client-certificate", false);
586   cfg->AddEntry<bool>("verify-peer", false);
587   cfg->AddEntry<bool>("is-handshaker-supported", false);
588   cfg->AddEntry<std::string>("handshaker-path", "");  // Ignore this
589   cfg->AddEntry<std::string>("advertise-alpn", "");
590   cfg->AddEntry<std::string>("expect-alpn", "");
591   cfg->AddEntry<std::vector<int>>("signing-prefs", std::vector<int>());
592   cfg->AddEntry<std::vector<int>>("verify-prefs", std::vector<int>());
593   cfg->AddEntry<int>("expect-peer-signature-algorithm", 0);
594   cfg->AddEntry<std::string>("nss-cipher", "");
595 
596   auto rv = cfg->ParseArgs(argc, argv);
597   switch (rv) {
598     case Config::kOK:
599       break;
600     case Config::kUnknownFlag:
601       exitCodeUnimplemented = true;
602     default:
603       return nullptr;
604   }
605 
606   // Needed to change to std::unique_ptr<const Config>
607   return std::move(cfg);
608 }
609 
RunCycle(std::unique_ptr<const Config> & cfg)610 bool RunCycle(std::unique_ptr<const Config>& cfg) {
611   std::unique_ptr<TestAgent> agent(TestAgent::Create(*cfg));
612   return agent && agent->DoExchange() == SECSuccess;
613 }
614 
GetExitCode(bool success)615 int GetExitCode(bool success) {
616   if (exitCodeUnimplemented) {
617     return 89;
618   }
619 
620   if (success) {
621     return 0;
622   }
623 
624   return 1;
625 }
626 
main(int argc,char ** argv)627 int main(int argc, char** argv) {
628   std::unique_ptr<const Config> cfg = ReadConfig(argc, argv);
629   if (!cfg) {
630     return GetExitCode(false);
631   }
632 
633   if (cfg->get<bool>("is-handshaker-supported")) {
634     std::cout << "No\n";
635     return 0;
636   }
637 
638   if (cfg->get<bool>("server")) {
639     if (SSL_ConfigServerSessionIDCache(1024, 0, 0, ".") != SECSuccess) {
640       std::cerr << "Couldn't configure session cache\n";
641       return 1;
642     }
643   }
644 
645   if (NSS_NoDB_Init(nullptr) != SECSuccess) {
646     return 1;
647   }
648 
649   // Run a single test cycle.
650   bool success = RunCycle(cfg);
651 
652   int resume_count = cfg->get<int>("resume-count");
653   while (success && resume_count-- > 0) {
654     std::cout << "Resuming" << std::endl;
655     success = RunCycle(cfg);
656   }
657 
658   SSL_ClearSessionCache();
659 
660   if (cfg->get<bool>("server")) {
661     SSL_ShutdownServerSessionIDCache();
662   }
663 
664   if (NSS_Shutdown() != SECSuccess) {
665     success = false;
666   }
667 
668   return GetExitCode(success);
669 }
670