Bug 1005142 - Part 1/2 - Add OCSP get capabilities to OCSPRequestor. r=keeler
authorCamilo Viecco <cviecco@mozilla.com>
Wed, 21 May 2014 15:42:21 -0700
changeset 197959 c288e2c355abaa840d36f1b754708bb466df767f
parent 197958 8a1273ab076750ec485984bc2e551a954085fd0c
child 197960 21574b16ad1eeb3103e9b3abf02c54ab71018b22
push idunknown
push userunknown
push dateunknown
reviewerskeeler
bugs1005142
milestone32.0a1
Bug 1005142 - Part 1/2 - Add OCSP get capabilities to OCSPRequestor. r=keeler
security/certverifier/CertVerifier.cpp
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/OCSPRequestor.cpp
security/certverifier/OCSPRequestor.h
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -369,33 +369,35 @@ CertVerifier::MozillaPKIXVerifyCert(
   callbackContainer.isChainValidArg = callbackState;
 
   NSSCertDBTrustDomain::OCSPFetching ocspFetching
     = !mOCSPDownloadEnabled ||
       (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
     : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
                                 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
 
+  ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
+                                                  : ocsp_get_disabled;
+
   SECStatus rv;
-
   // TODO(bug 970750): anyExtendedKeyUsage
   // TODO: encipherOnly/decipherOnly
   // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
   // S/MIME EKU:       http://tools.ietf.org/html/rfc3850#section-4.4.4
 
   // TODO(bug 915931): Pass in stapled OCSP response in all calls to
   //                   BuildCertChain.
 
   mozilla::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg);
+                                       pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse,
                           builtChain);
       break;
     }
 
@@ -410,17 +412,17 @@ CertVerifier::MozillaPKIXVerifyCert(
       SECOidTag evPolicyOidTag;
       rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
       if (rv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
-                      mOCSPCache, pinArg, &callbackContainer);
+                      mOCSPCache, pinArg, ocspGETConfig, &callbackContainer);
         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                           KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                           KU_KEY_ENCIPHERMENT, // RSA
                                           KU_KEY_AGREEMENT, // ECDH/DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse,
                                           builtChain);
         if (rv == SECSuccess) {
@@ -436,67 +438,68 @@ CertVerifier::MozillaPKIXVerifyCert(
       if (flags & FLAG_MUST_BE_EV) {
         PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
         rv = SECFailure;
         break;
       }
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg, &callbackContainer);
+                                       pinArg, ocspGETConfig,
+                                       &callbackContainer);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         KeyPurposeId::id_kp_serverAuth,
                                         CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg);
+                                       pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
                           KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg);
+                                       pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg);
+                                       pinArg, ocspGETConfig);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         0,
                                         KeyPurposeId::id_kp_emailProtection,
                                         CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
-                                       mOCSPCache, pinArg);
+                                       mOCSPCache, pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
@@ -515,30 +518,30 @@ CertVerifier::MozillaPKIXVerifyCert(
         eku = KeyPurposeId::anyExtendedKeyUsage;
       } else {
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
-                                    pinArg);
+                                    pinArg, ocspGETConfig);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
-                                        pinArg);
+                                        pinArg, ocspGETConfig);
         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
                             eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse, builtChain);
         if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
-                                                  pinArg);
+                                                  pinArg, ocspGETConfig);
           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
                               keyUsage, eku, CertPolicyId::anyPolicy,
                               stapledOCSPResponse, builtChain);
         }
       }
 
       break;
     }
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -39,21 +39,23 @@ namespace {
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
                                            OCSPFetching ocspFetching,
                                            OCSPCache& ocspCache,
                                            void* pinArg,
+                                           CertVerifier::ocsp_get_config ocspGETConfig,
                                            CERTChainVerifyCallback* checkChainCallback)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
+  , mOCSPGetConfig(ocspGETConfig)
   , mCheckChainCallback(checkChainCallback)
 {
 }
 
 SECStatus
 NSSCertDBTrustDomain::FindPotentialIssuers(
   const SECItem* encodedIssuerName, PRTime time,
   /*out*/ mozilla::pkix::ScopedCERTCertList& results)
@@ -333,17 +335,18 @@ NSSCertDBTrustDomain::CheckRevocation(
       cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
     const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
                                                     issuerCert));
     if (!request) {
       return SECFailure;
     }
 
     response = DoOCSPRequest(arena.get(), url.get(), request,
-                             OCSPFetchingTypeToTimeoutTime(mOCSPFetching));
+                             OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
+                             mOCSPGetConfig == CertVerifier::ocsp_get_enabled);
   }
 
   if (!response) {
     PRErrorCode error = PR_GetError();
     if (error == 0) {
       error = cachedResponseErrorCode;
     }
     PRTime timeout = time + ServerFailureDelay;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -53,16 +53,17 @@ public:
     NeverFetchOCSP = 0,
     FetchOCSPForDVSoftFail = 1,
     FetchOCSPForDVHardFail = 2,
     FetchOCSPForEV = 3,
     LocalOnlyOCSPForEV = 4,
   };
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
+                       CertVerifier::ocsp_get_config ocspGETConfig,
                        CERTChainVerifyCallback* checkChainCallback = nullptr);
 
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
                         PRTime time,
                 /*out*/ mozilla::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
@@ -90,14 +91,15 @@ private:
   SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
     const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
     const SECItem* encodedResponse, EncodedResponseSource responseSource);
 
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
+  const CertVerifier::ocsp_get_config mOCSPGetConfig;
   CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/certverifier/OCSPRequestor.cpp
+++ b/security/certverifier/OCSPRequestor.cpp
@@ -1,23 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "OCSPRequestor.h"
 
+#include "mozilla/Base64.h"
 #include "nsIURLParser.h"
 #include "nsNSSCallbacks.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "pkix/ScopedPtr.h"
 #include "secerr.h"
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gCertVerifierLog;
+#endif
+
 namespace mozilla { namespace psm {
 
 using mozilla::pkix::ScopedPtr;
 
 void
 ReleaseHttpServerSession(nsNSSHttpServerSession* httpServerSession)
 {
   delete httpServerSession;
@@ -28,18 +33,46 @@ typedef ScopedPtr<nsNSSHttpServerSession
 void
 ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession)
 {
   httpRequestSession->Release();
 }
 typedef ScopedPtr<nsNSSHttpRequestSession, ReleaseHttpRequestSession>
   ScopedHTTPRequestSession;
 
-SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
-                       const SECItem* encodedRequest, PRIntervalTime timeout)
+static nsresult
+AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path)
+{
+  nsresult rv;
+  nsDependentCSubstring requestAsSubstring(
+    reinterpret_cast<const char*>(encodedRequest->data), encodedRequest->len);
+  nsCString base64Request;
+  rv = Base64Encode(requestAsSubstring, base64Request);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+         ("Setting up OCSP GET path, pre path =%s\n",
+          PromiseFlatCString(path).get()));
+
+  // The path transformation is not a direct url encoding. Three characters
+  // need change '+' -> "%2B", '/' -> "%2F", and '=' -> '%3D'.
+  // http://tools.ietf.org/html/rfc5019#section-5
+  base64Request.ReplaceSubstring("+", "%2B");
+  base64Request.ReplaceSubstring("/", "%2F");
+  base64Request.ReplaceSubstring("=", "%3D");
+  path.Append(base64Request);
+  return NS_OK;
+}
+
+SECItem*
+DoOCSPRequest(PLArenaPool* arena, const char* url,
+              const SECItem* encodedRequest, PRIntervalTime timeout,
+              bool useGET)
 {
   nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
   if (!urlParser) {
     PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
     return nullptr;
   }
 
   uint32_t schemePos;
@@ -81,49 +114,66 @@ SECItem* DoOCSPRequest(PLArenaPool* aren
   }
   if (hostnameLen < 0) {
     PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
     return nullptr;
   }
   if (port == -1) {
     port = 80;
   }
+  nsAutoCString hostname(url + authorityPos + hostnamePos, hostnameLen);
 
-  nsAutoCString hostname(url + authorityPos + hostnamePos, hostnameLen);
   SEC_HTTP_SERVER_SESSION serverSessionPtr = nullptr;
   if (nsNSSHttpInterface::createSessionFcn(hostname.BeginReading(), port,
                                            &serverSessionPtr) != SECSuccess) {
     PR_SetError(SEC_ERROR_NO_MEMORY, 0);
     return nullptr;
   }
-
   ScopedHTTPServerSession serverSession(
     reinterpret_cast<nsNSSHttpServerSession*>(serverSessionPtr));
+
   nsAutoCString path;
   if (pathLen > 0) {
     path.Assign(url + pathPos, pathLen);
   } else {
     path.Assign("/");
   }
-  SEC_HTTP_REQUEST_SESSION requestSessionPtr;
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+         ("Setting up OCSP request: pre all path =%s  pathlen=%d\n", path.get(),
+          pathLen));
+  nsAutoCString method("POST");
+  if (useGET) {
+    method.Assign("GET");
+    if (!StringEndsWith(path, NS_LITERAL_CSTRING("/"))) {
+      path.Append("/");
+    }
+    nsresult rv = AppendEscapedBase64Item(encodedRequest, path);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  }
+
+  SEC_HTTP_REQUEST_SESSION requestSessionPtr = nullptr;
   if (nsNSSHttpInterface::createFcn(serverSession.get(), "http",
-                                    path.BeginReading(), "POST",
+                                    path.get(), method.get(),
                                     timeout, &requestSessionPtr)
         != SECSuccess) {
     PR_SetError(SEC_ERROR_NO_MEMORY, 0);
     return nullptr;
   }
-
   ScopedHTTPRequestSession requestSession(
     reinterpret_cast<nsNSSHttpRequestSession*>(requestSessionPtr));
-  if (nsNSSHttpInterface::setPostDataFcn(requestSession.get(),
-        reinterpret_cast<char*>(encodedRequest->data), encodedRequest->len,
-        "application/ocsp-request") != SECSuccess) {
-    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
-    return nullptr;
+
+  if (!useGET) {
+    if (nsNSSHttpInterface::setPostDataFcn(requestSession.get(),
+          reinterpret_cast<char*>(encodedRequest->data), encodedRequest->len,
+          "application/ocsp-request") != SECSuccess) {
+      PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+      return nullptr;
+    }
   }
 
   uint16_t httpResponseCode;
   const char* httpResponseData;
   uint32_t httpResponseDataLen = 0; // 0 means any response size is acceptable
   if (nsNSSHttpInterface::trySendAndReceiveFcn(requestSession.get(), nullptr,
                                                &httpResponseCode, nullptr,
                                                nullptr, &httpResponseData,
--- a/security/certverifier/OCSPRequestor.h
+++ b/security/certverifier/OCSPRequestor.h
@@ -2,19 +2,21 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_psm_OCSPRequestor_h
 #define mozilla_psm_OCSPRequestor_h
 
+#include "CertVerifier.h"
 #include "secmodt.h"
 
 namespace mozilla { namespace psm {
 
 // The memory returned is owned by the given arena.
 SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
-                       const SECItem* encodedRequest, PRIntervalTime timeout);
+                       const SECItem* encodedRequest, PRIntervalTime timeout,
+                       bool useGET);
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm_OCSPRequestor_h