tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }