OCSPStaplingServer.cpp (9821B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // This is a standalone server that delivers various stapled OCSP responses. 6 // The client is expected to connect, initiate an SSL handshake (with SNI 7 // to indicate which "server" to connect to), and verify the OCSP response. 8 // If all is good, the client then sends one encrypted byte and receives that 9 // same byte back. 10 // This server also has the ability to "call back" another process waiting on 11 // it. That is, when the server is all set up and ready to receive connections, 12 // it will connect to a specified port and issue a simple HTTP request. 13 14 #include <fstream> 15 #include <stdio.h> 16 17 #include "OCSPCommon.h" 18 #include "TLSServer.h" 19 20 using namespace mozilla; 21 using namespace mozilla::pkix::test; 22 using namespace mozilla::test; 23 24 const OCSPHost sOCSPHosts[] = { 25 {"ocsp-stapling-good.example.com", ORTGood, nullptr, nullptr}, 26 {"ocsp-stapling-revoked.example.com", ORTRevoked, nullptr, nullptr}, 27 {"ocsp-stapling-revoked-old.example.com", ORTRevokedOld, nullptr, nullptr}, 28 {"ocsp-stapling-unknown.example.com", ORTUnknown, nullptr, nullptr}, 29 {"ocsp-stapling-unknown-old.example.com", ORTUnknownOld, nullptr, nullptr}, 30 {"ocsp-stapling-good-other.example.com", ORTGoodOtherCert, 31 "ocspOtherEndEntity", nullptr}, 32 {"ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "other-test-ca", 33 nullptr}, 34 {"ocsp-stapling-expired.example.com", ORTExpired, nullptr, nullptr}, 35 {"ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr, 36 nullptr}, 37 {"ocsp-stapling-none.example.com", ORTNone, nullptr, nullptr}, 38 {"ocsp-stapling-empty.example.com", ORTEmpty, nullptr, nullptr}, 39 {"ocsp-stapling-malformed.example.com", ORTMalformed, nullptr, nullptr}, 40 {"ocsp-stapling-srverr.example.com", ORTSrverr, nullptr, nullptr}, 41 {"ocsp-stapling-trylater.example.com", ORTTryLater, nullptr, nullptr}, 42 {"ocsp-stapling-needssig.example.com", ORTNeedsSig, nullptr, nullptr}, 43 {"ocsp-stapling-unauthorized.example.com", ORTUnauthorized, nullptr, 44 nullptr}, 45 {"ocsp-stapling-with-intermediate.example.com", ORTGood, nullptr, 46 "ocspEEWithIntermediate"}, 47 {"ocsp-stapling-bad-signature.example.com", ORTBadSignature, nullptr, 48 nullptr}, 49 {"ocsp-stapling-skip-responseBytes.example.com", ORTSkipResponseBytes, 50 nullptr, nullptr}, 51 {"ocsp-stapling-critical-extension.example.com", ORTCriticalExtension, 52 nullptr, nullptr}, 53 {"ocsp-stapling-noncritical-extension.example.com", ORTNoncriticalExtension, 54 nullptr, nullptr}, 55 {"ocsp-stapling-empty-extensions.example.com", ORTEmptyExtensions, nullptr, 56 nullptr}, 57 {"ocsp-stapling-delegated-included.example.com", ORTDelegatedIncluded, 58 "delegatedSigner", nullptr}, 59 {"ocsp-stapling-delegated-included-last.example.com", 60 ORTDelegatedIncludedLast, "delegatedSigner", nullptr}, 61 {"ocsp-stapling-delegated-missing.example.com", ORTDelegatedMissing, 62 "delegatedSigner", nullptr}, 63 {"ocsp-stapling-delegated-missing-multiple.example.com", 64 ORTDelegatedMissingMultiple, "delegatedSigner", nullptr}, 65 {"ocsp-stapling-delegated-no-extKeyUsage.example.com", ORTDelegatedIncluded, 66 "invalidDelegatedSignerNoExtKeyUsage", nullptr}, 67 {"ocsp-stapling-delegated-from-intermediate.example.com", 68 ORTDelegatedIncluded, "invalidDelegatedSignerFromIntermediate", nullptr}, 69 {"ocsp-stapling-delegated-keyUsage-crlSigning.example.com", 70 ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning", nullptr}, 71 {"ocsp-stapling-delegated-wrong-extKeyUsage.example.com", 72 ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage", nullptr}, 73 {"ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired, 74 nullptr, nullptr}, 75 {"keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, 76 "rsa-1016-keysizeDelegatedSigner", nullptr}, 77 {"revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked, 78 "ca-used-as-end-entity", nullptr}, 79 {"ocsp-stapling-must-staple.example.com", ORTGood, nullptr, 80 "must-staple-ee"}, 81 {"ocsp-stapling-must-staple-revoked.example.com", ORTRevoked, nullptr, 82 "must-staple-ee"}, 83 {"ocsp-stapling-must-staple-missing.example.com", ORTNone, nullptr, 84 "must-staple-ee"}, 85 {"ocsp-stapling-must-staple-empty.example.com", ORTEmpty, nullptr, 86 "must-staple-ee"}, 87 {"ocsp-stapling-must-staple-ee-with-must-staple-int.example.com", ORTGood, 88 nullptr, "must-staple-ee-with-must-staple-int"}, 89 {"ocsp-stapling-plain-ee-with-must-staple-int.example.com", ORTGood, 90 nullptr, "must-staple-missing-ee"}, 91 {"ocsp-stapling-must-staple-expired.example.com", ORTExpired, nullptr, 92 "must-staple-ee"}, 93 {"ocsp-stapling-must-staple-try-later.example.com", ORTTryLater, nullptr, 94 "must-staple-ee"}, 95 {"ocsp-stapling-must-staple-invalid-signer.example.com", ORTGoodOtherCA, 96 "other-test-ca", "must-staple-ee"}, 97 {"multi-tls-feature-good.example.com", ORTNone, nullptr, 98 "multi-tls-feature-good-ee"}, 99 {"multi-tls-feature-bad.example.com", ORTNone, nullptr, 100 "multi-tls-feature-bad-ee"}, 101 {nullptr, ORTNull, nullptr, nullptr}}; 102 103 enum class SCTsVia { 104 None, 105 OCSP, 106 TLS, 107 }; 108 109 struct CTHost { 110 const char* mHostName; 111 std::vector<const char*> mSCTFilenames; 112 SCTsVia mSCTsVia; 113 }; 114 115 MOZ_RUNINIT const CTHost sCTHosts[] = { 116 {"ct-via-ocsp.example.com", 117 {"test_ct/ct-via-ocsp-1.sct", "test_ct/ct-via-ocsp-2.sct"}, 118 SCTsVia::OCSP}, 119 {"ct-via-tls.example.com", 120 {"test_ct/ct-via-tls-1.sct", "test_ct/ct-via-tls-2.sct"}, 121 SCTsVia::TLS}, 122 {"ct-tampered.example.com", 123 {"test_ct/ct-tampered-1.sct", "test_ct/ct-tampered-2.sct"}, 124 SCTsVia::TLS}, 125 {nullptr, {}, SCTsVia::None}}; 126 127 ByteString ReadSCTList(const std::vector<const char*>& sctFilenames) { 128 std::vector<std::string> scts; 129 for (const auto& sctFilename : sctFilenames) { 130 std::ifstream in(sctFilename, std::ios::binary); 131 if (in.bad() || !in.is_open()) { 132 if (gDebugLevel >= DEBUG_ERRORS) { 133 fprintf(stderr, "couldn't open '%s'\n", sctFilename); 134 return ByteString(); 135 } 136 } 137 std::ostringstream contentsStream; 138 contentsStream << in.rdbuf(); 139 std::string contents = contentsStream.str(); 140 scts.push_back(std::move(contents)); 141 } 142 143 ByteString contents; 144 for (const auto& sct : scts) { 145 // Each SCT has a 2-byte length prefix. 146 contents.push_back(sct.length() / 256); 147 contents.push_back(sct.length() % 256); 148 contents.append(reinterpret_cast<const uint8_t*>(sct.data()), sct.length()); 149 } 150 // The entire SCT list also has a 2-byte length prefix. 151 ByteString sctList; 152 sctList.push_back(contents.length() / 256); 153 sctList.push_back(contents.length() % 256); 154 sctList.append(reinterpret_cast<const uint8_t*>(contents.data()), 155 contents.length()); 156 return sctList; 157 } 158 159 int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr, 160 uint32_t aSrvNameArrSize, void* aArg) { 161 const char* hostName = nullptr; 162 OCSPResponseType ocspResponseType = ORTNone; 163 const char* additionalCertName = nullptr; 164 const char* serverCertName = nullptr; 165 ByteString sctList; 166 SCTsVia sctsVia = SCTsVia::None; 167 168 const OCSPHost* host = 169 GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sOCSPHosts); 170 if (host) { 171 hostName = host->mHostName; 172 ocspResponseType = host->mORT; 173 additionalCertName = host->mAdditionalCertName; 174 serverCertName = host->mServerCertName; 175 } else { 176 const CTHost* ctHost = 177 GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sCTHosts); 178 if (!ctHost) { 179 return SSL_SNI_SEND_ALERT; 180 } 181 hostName = ctHost->mHostName; 182 ocspResponseType = ORTGood; 183 serverCertName = ctHost->mHostName; 184 sctList = ReadSCTList(ctHost->mSCTFilenames); 185 if (sctList.empty()) { 186 return SSL_SNI_SEND_ALERT; 187 } 188 sctsVia = ctHost->mSCTsVia; 189 } 190 191 if (gDebugLevel >= DEBUG_VERBOSE) { 192 fprintf(stderr, "found pre-defined host '%s'\n", hostName); 193 } 194 195 const char* certNickname = 196 serverCertName ? serverCertName : DEFAULT_CERT_NICKNAME; 197 198 UniqueCERTCertificate cert; 199 SSLKEAType certKEA; 200 if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, certNickname, &cert, 201 &certKEA, nullptr)) { 202 return SSL_SNI_SEND_ALERT; 203 } 204 205 // If the OCSP response type is "none", don't staple a response. 206 if (ocspResponseType == ORTNone) { 207 return 0; 208 } 209 210 UniquePLArenaPool arena(PORT_NewArena(1024)); 211 if (!arena) { 212 PrintPRError("PORT_NewArena failed"); 213 return SSL_SNI_SEND_ALERT; 214 } 215 216 // response is contained by the arena - freeing the arena will free it 217 SECItemArray* response = 218 GetOCSPResponseForType(ocspResponseType, cert, arena, additionalCertName, 219 0, sctsVia == SCTsVia::OCSP ? &sctList : nullptr); 220 if (!response) { 221 return SSL_SNI_SEND_ALERT; 222 } 223 224 // SSL_SetStapledOCSPResponses makes a deep copy of response 225 SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA); 226 if (st != SECSuccess) { 227 PrintPRError("SSL_SetStapledOCSPResponses failed"); 228 return SSL_SNI_SEND_ALERT; 229 } 230 231 if (sctsVia == SCTsVia::TLS) { 232 SECItem scts = {siBuffer, const_cast<unsigned char*>(sctList.data()), 233 (unsigned int)sctList.size()}; 234 st = SSL_SetSignedCertTimestamps(aFd, &scts, certKEA); 235 if (st != SECSuccess) { 236 PrintPRError("SSL_SetSignedCertTimestamps failed"); 237 return SSL_SNI_SEND_ALERT; 238 } 239 } 240 241 return 0; 242 } 243 244 int main(int argc, char* argv[]) { 245 return StartServer(argc, argv, DoSNISocketConfig, nullptr); 246 }