// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert_net/nss_ocsp.h" #include #include #include #include #include #include #include #include #include #include #include #include "base/callback.h" #include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/request_priority.h" #include "net/base/upload_bytes_element_reader.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/redirect_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "url/gurl.h" namespace net { namespace { // Protects |g_request_context|. pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER; URLRequestContext* g_request_context = NULL; // The default timeout for network fetches in NSS is 60 seconds. Choose a // saner upper limit for OCSP/CRL/AIA fetches. const int kNetworkFetchTimeoutInSecs = 15; class OCSPRequestSession; class OCSPIOLoop { public: // This class is only instantiated as a leaky LazyInstance, so its destructor // is never called. ~OCSPIOLoop() = delete; void StartUsing() { base::AutoLock autolock(lock_); DCHECK(base::MessageLoopForIO::IsCurrent()); io_task_runner_ = base::ThreadTaskRunnerHandle::Get(); } // Called on IO loop. void Shutdown(); // Called from worker thread. void PostTaskToIOLoop(const base::Location& from_here, const base::Closure& task); void AddRequest(OCSPRequestSession* request); void RemoveRequest(OCSPRequestSession* request); private: friend struct base::LazyInstanceTraitsBase; OCSPIOLoop() = default; void CancelAllRequests(); // Protects all members below. mutable base::Lock lock_; std::set requests_; scoped_refptr io_task_runner_; DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop); }; base::LazyInstance::Leaky g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER; const int kRecvBufferSize = 4096; // All OCSP handlers should be called in the context of // CertVerifier's thread (i.e. worker pool, not on the I/O thread). // It supports blocking mode only. SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, SEC_HTTP_SERVER_SESSION* pSession); SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, PRPollDesc **pPollDesc); SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session); SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, const char* http_protocol_variant, const char* path_and_query_string, const char* http_request_method, const PRIntervalTime timeout, SEC_HTTP_REQUEST_SESSION* pRequest); SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, const char* http_data, const PRUint32 http_data_len, const char* http_content_type); SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, const char* http_header_name, const char* http_header_value); SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, PRPollDesc** pPollDesc, PRUint16* http_response_code, const char** http_response_content_type, const char** http_response_headers, const char** http_response_data, PRUint32* http_response_data_len); SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); char* GetAlternateOCSPAIAInfo(CERTCertificate *cert); class OCSPNSSInitialization { private: friend struct base::LazyInstanceTraitsBase; OCSPNSSInitialization(); // This class is only instantiated as a leaky LazyInstance, so its destructor // is never called. ~OCSPNSSInitialization() = delete; SEC_HttpClientFcn client_fcn_; DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization); }; base::LazyInstance::Leaky g_ocsp_nss_initialization = LAZY_INSTANCE_INITIALIZER; // Concrete class for SEC_HTTP_REQUEST_SESSION. // Public methods except virtual methods of URLRequest::Delegate // (On* methods) run on certificate verifier thread (worker thread). // Virtual methods of URLRequest::Delegate and private methods run // on IO thread. class OCSPRequestSession : public base::RefCountedThreadSafe, public URLRequest::Delegate { public: OCSPRequestSession(const GURL& url, const char* http_request_method, base::TimeDelta timeout) : url_(url), http_request_method_(http_request_method), timeout_(timeout), buffer_(new IOBuffer(kRecvBufferSize)), response_code_(-1), cv_(&lock_), finished_(false) {} void SetPostData(const char* http_data, PRUint32 http_data_len, const char* http_content_type) { // |upload_content_| should not be modified if |request_| is active. DCHECK(!request_); upload_content_.assign(http_data, http_data_len); upload_content_type_.assign(http_content_type); } void AddHeader(const char* http_header_name, const char* http_header_value) { extra_request_headers_.SetHeader(http_header_name, http_header_value); } void Start() { // At this point, it runs on worker thread. // |io_task_runner_| is only initialized in StartURLRequest, so no need to // lock |lock_| here. DCHECK(!io_task_runner_); g_ocsp_io_loop.Get().PostTaskToIOLoop( FROM_HERE, base::Bind(&OCSPRequestSession::StartURLRequest, this)); } bool Started() const { return request_.get() != NULL; } void Cancel() { // IO thread may reset |io_task_runner_|, so protect by |lock_|. base::AutoLock autolock(lock_); CancelLocked(); } bool Finished() const { base::AutoLock autolock(lock_); return finished_; } bool Wait() { base::TimeDelta timeout = timeout_; base::AutoLock autolock(lock_); while (!finished_) { base::TimeTicks last_time = base::TimeTicks::Now(); cv_.TimedWait(timeout); // Check elapsed time base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time; timeout -= elapsed_time; if (timeout < base::TimeDelta()) { VLOG(1) << "OCSP Timed out"; if (!finished_) CancelLocked(); break; } } return finished_; } const GURL& url() const { return url_; } const std::string& http_request_method() const { return http_request_method_; } base::TimeDelta timeout() const { return timeout_; } PRUint16 http_response_code() const { DCHECK(finished_); return response_code_; } const std::string& http_response_content_type() const { DCHECK(finished_); return response_content_type_; } const std::string& http_response_headers() const { DCHECK(finished_); return response_headers_->raw_headers(); } const std::string& http_response_data() const { DCHECK(finished_); return data_; } void OnReceivedRedirect(URLRequest* request, const RedirectInfo& redirect_info, bool* defer_redirect) override { DCHECK_EQ(request_.get(), request); DCHECK(io_task_runner_->BelongsToCurrentThread()); if (!redirect_info.new_url.SchemeIs("http")) { // Prevent redirects to non-HTTP schemes, including HTTPS. This matches // the initial check in OCSPServerSession::CreateRequest(). CancelURLRequest(); } } void OnResponseStarted(URLRequest* request, int net_error) override { DCHECK_EQ(request_.get(), request); DCHECK(io_task_runner_->BelongsToCurrentThread()); DCHECK_NE(ERR_IO_PENDING, net_error); int bytes_read = 0; if (net_error == OK) { response_code_ = request_->GetResponseCode(); response_headers_ = request_->response_headers(); response_headers_->GetMimeType(&response_content_type_); bytes_read = request_->Read(buffer_.get(), kRecvBufferSize); } OnReadCompleted(request_.get(), bytes_read); } void OnReadCompleted(URLRequest* request, int bytes_read) override { DCHECK_EQ(request_.get(), request); DCHECK(io_task_runner_->BelongsToCurrentThread()); while (bytes_read > 0) { data_.append(buffer_->data(), bytes_read); bytes_read = request_->Read(buffer_.get(), kRecvBufferSize); } if (bytes_read != ERR_IO_PENDING) { request_.reset(); g_ocsp_io_loop.Get().RemoveRequest(this); { base::AutoLock autolock(lock_); finished_ = true; io_task_runner_ = nullptr; } cv_.Signal(); Release(); // Balanced with StartURLRequest(). } } // Must be called on the IO loop thread. void CancelURLRequest() { #ifndef NDEBUG { base::AutoLock autolock(lock_); if (io_task_runner_) DCHECK(io_task_runner_->BelongsToCurrentThread()); } #endif if (request_) { request_.reset(); g_ocsp_io_loop.Get().RemoveRequest(this); { base::AutoLock autolock(lock_); finished_ = true; io_task_runner_ = nullptr; } cv_.Signal(); Release(); // Balanced with StartURLRequest(). } } private: friend class base::RefCountedThreadSafe; ~OCSPRequestSession() override { // When this destructor is called, there should be only one thread that has // a reference to this object, and so that thread doesn't need to lock // |lock_| here. DCHECK(!request_); DCHECK(!io_task_runner_); } // Must call this method while holding |lock_|. void CancelLocked() { lock_.AssertAcquired(); if (io_task_runner_) { io_task_runner_->PostTask( FROM_HERE, base::Bind(&OCSPRequestSession::CancelURLRequest, this)); } } // Runs on |g_ocsp_io_loop|'s IO loop. void StartURLRequest() { DCHECK(!request_); pthread_mutex_lock(&g_request_context_lock); URLRequestContext* url_request_context = g_request_context; pthread_mutex_unlock(&g_request_context_lock); if (url_request_context == NULL) return; { base::AutoLock autolock(lock_); DCHECK(!io_task_runner_); DCHECK(base::MessageLoopForIO::IsCurrent()); io_task_runner_ = base::ThreadTaskRunnerHandle::Get(); g_ocsp_io_loop.Get().AddRequest(this); } net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("ocsp_start_url_request", R"( semantics { sender: "OCSP" description: "Verifying the revocation status of a certificate via OCSP." trigger: "This may happen in response to visiting a website that uses " "https://" data: "Identifier for the certificate whose revocation status is being " "checked. See https://tools.ietf.org/html/rfc6960#section-2.1 for " "more details." destination: OTHER } policy { cookies_allowed: NO setting: "This feature cannot be disabled by settings." policy_exception_justification: "Not implemented." })"); request_ = url_request_context->CreateRequest(url_, DEFAULT_PRIORITY, this, traffic_annotation); // To meet the privacy requirements of incognito mode. request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES | LOAD_DO_NOT_SEND_COOKIES); if (http_request_method_ == "POST") { DCHECK(!upload_content_.empty()); DCHECK(!upload_content_type_.empty()); request_->set_method("POST"); extra_request_headers_.SetHeader( HttpRequestHeaders::kContentType, upload_content_type_); std::unique_ptr reader(new UploadBytesElementReader( upload_content_.data(), upload_content_.size())); request_->set_upload( ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); } if (!extra_request_headers_.IsEmpty()) request_->SetExtraRequestHeaders(extra_request_headers_); request_->Start(); AddRef(); // Release after |request_| deleted. } GURL url_; // The URL we eventually wound up at std::string http_request_method_; base::TimeDelta timeout_; // The timeout for OCSP std::unique_ptr request_; // The actual request this wraps scoped_refptr buffer_; // Read buffer HttpRequestHeaders extra_request_headers_; // HTTP POST payload. |request_| reads bytes from this. std::string upload_content_; std::string upload_content_type_; // MIME type of POST payload int response_code_; // HTTP status code for the request std::string response_content_type_; scoped_refptr response_headers_; std::string data_; // Results of the request // |lock_| protects |finished_| and |io_task_runner_|. mutable base::Lock lock_; base::ConditionVariable cv_; // TaskRunner for the IO thread. Set when StartURLRequest() is invoked (on the // IO thread). scoped_refptr io_task_runner_; bool finished_; DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession); }; // Concrete class for SEC_HTTP_SERVER_SESSION. class OCSPServerSession { public: OCSPServerSession(const char* host, PRUint16 port) : host_and_port_(host, port) {} ~OCSPServerSession() = default; OCSPRequestSession* CreateRequest(const char* http_protocol_variant, const char* path_and_query_string, const char* http_request_method, const PRIntervalTime timeout) { // We dont' support "https" because we haven't thought about // whether it's safe to re-enter this code from talking to an OCSP // responder over SSL. if (strcmp(http_protocol_variant, "http") != 0) { PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); return NULL; } std::string url_string(base::StringPrintf( "%s://%s%s", http_protocol_variant, host_and_port_.ToString().c_str(), path_and_query_string)); VLOG(1) << "URL [" << url_string << "]"; GURL url(url_string); // NSS does not expose public functions to adjust the fetch timeout when // using libpkix, so hardcode the upper limit for network fetches. base::TimeDelta actual_timeout = std::min( base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs), base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout))); return new OCSPRequestSession(url, http_request_method, actual_timeout); } private: HostPortPair host_and_port_; DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); }; void OCSPIOLoop::Shutdown() { // Safe to read outside lock since we only write on IO thread anyway. DCHECK(io_task_runner_->BelongsToCurrentThread()); // Prevent the worker thread from trying to access |io_task_runner_|. { base::AutoLock autolock(lock_); io_task_runner_ = nullptr; } CancelAllRequests(); pthread_mutex_lock(&g_request_context_lock); g_request_context = NULL; pthread_mutex_unlock(&g_request_context_lock); } void OCSPIOLoop::PostTaskToIOLoop(const base::Location& from_here, const base::Closure& task) { base::AutoLock autolock(lock_); if (io_task_runner_) io_task_runner_->PostTask(from_here, task); } void OCSPIOLoop::AddRequest(OCSPRequestSession* request) { DCHECK(!base::ContainsKey(requests_, request)); requests_.insert(request); } void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) { DCHECK(base::ContainsKey(requests_, request)); requests_.erase(request); } void OCSPIOLoop::CancelAllRequests() { // CancelURLRequest() always removes the request from the requests_ // set synchronously. while (!requests_.empty()) (*requests_.begin())->CancelURLRequest(); } OCSPNSSInitialization::OCSPNSSInitialization() { // NSS calls the functions in the function table to download certificates // or CRLs or talk to OCSP responders over HTTP. These functions must // set an NSS/NSPR error code when they fail. Otherwise NSS will get the // residual error code from an earlier failed function call. client_fcn_.version = 1; SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1; ft->createSessionFcn = OCSPCreateSession; ft->keepAliveSessionFcn = OCSPKeepAliveSession; ft->freeSessionFcn = OCSPFreeSession; ft->createFcn = OCSPCreate; ft->setPostDataFcn = OCSPSetPostData; ft->addHeaderFcn = OCSPAddHeader; ft->trySendAndReceiveFcn = OCSPTrySendAndReceive; ft->cancelFcn = NULL; ft->freeFcn = OCSPFree; SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_); if (status != SECSuccess) { NOTREACHED() << "Error initializing OCSP: " << PR_GetError(); } // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the // CRLs for Network Solutions Certificate Authority have bad signatures, // which causes certificates issued by that CA to be reported as revoked. // By using OCSP for those certificates, which don't have AIA extensions, // we can work around these bugs. See http://crbug.com/41730. CERT_StringFromCertFcn old_callback = NULL; status = CERT_RegisterAlternateOCSPAIAInfoCallBack( GetAlternateOCSPAIAInfo, &old_callback); if (status == SECSuccess) { DCHECK(!old_callback); } else { NOTREACHED() << "Error initializing OCSP: " << PR_GetError(); } } // OCSP Http Client functions. // Our Http Client functions operate in blocking mode. SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, SEC_HTTP_SERVER_SESSION* pSession) { VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum; pthread_mutex_lock(&g_request_context_lock); URLRequestContext* request_context = g_request_context; pthread_mutex_unlock(&g_request_context_lock); if (request_context == NULL) { LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host; // The application failed to call SetURLRequestContextForNSSHttpIO or // has already called ShutdownNSSHttpIO, so we can't create and use // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error // code for these error conditions, but is close enough. PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); return SECFailure; } *pSession = new OCSPServerSession(host, portnum); return SECSuccess; } SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, PRPollDesc **pPollDesc) { VLOG(1) << "OCSP keep alive"; if (pPollDesc) *pPollDesc = NULL; return SECSuccess; } SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) { VLOG(1) << "OCSP free session"; delete reinterpret_cast(session); return SECSuccess; } SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, const char* http_protocol_variant, const char* path_and_query_string, const char* http_request_method, const PRIntervalTime timeout, SEC_HTTP_REQUEST_SESSION* pRequest) { VLOG(1) << "OCSP create protocol=" << http_protocol_variant << " path_and_query=" << path_and_query_string << " http_request_method=" << http_request_method << " timeout=" << timeout; OCSPServerSession* ocsp_session = reinterpret_cast(session); OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant, path_and_query_string, http_request_method, timeout); SECStatus rv = SECFailure; if (req) { req->AddRef(); // Release in OCSPFree(). rv = SECSuccess; } *pRequest = req; return rv; } SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, const char* http_data, const PRUint32 http_data_len, const char* http_content_type) { VLOG(1) << "OCSP set post data len=" << http_data_len; OCSPRequestSession* req = reinterpret_cast(request); req->SetPostData(http_data, http_data_len, http_content_type); return SECSuccess; } SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, const char* http_header_name, const char* http_header_value) { VLOG(1) << "OCSP add header name=" << http_header_name << " value=" << http_header_value; OCSPRequestSession* req = reinterpret_cast(request); req->AddHeader(http_header_name, http_header_value); return SECSuccess; } // Sets response of |req| in the output parameters. // It is helper routine for OCSP trySendAndReceiveFcn. // |http_response_data_len| could be used as input parameter. If it has // non-zero value, it is considered as maximum size of |http_response_data|. SECStatus OCSPSetResponse(OCSPRequestSession* req, PRUint16* http_response_code, const char** http_response_content_type, const char** http_response_headers, const char** http_response_data, PRUint32* http_response_data_len) { DCHECK(req->Finished()); const std::string& data = req->http_response_data(); if (http_response_data_len && *http_response_data_len) { if (*http_response_data_len < data.size()) { LOG(ERROR) << "response body too large: " << *http_response_data_len << " < " << data.size(); *http_response_data_len = data.size(); PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); return SECFailure; } } VLOG(1) << "OCSP response " << " response_code=" << req->http_response_code() << " content_type=" << req->http_response_content_type() << " header=" << req->http_response_headers() << " data_len=" << data.size(); if (http_response_code) *http_response_code = req->http_response_code(); if (http_response_content_type) *http_response_content_type = req->http_response_content_type().c_str(); if (http_response_headers) *http_response_headers = req->http_response_headers().c_str(); if (http_response_data) *http_response_data = data.data(); if (http_response_data_len) *http_response_data_len = data.size(); return SECSuccess; } SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, PRPollDesc** pPollDesc, PRUint16* http_response_code, const char** http_response_content_type, const char** http_response_headers, const char** http_response_data, PRUint32* http_response_data_len) { if (http_response_data_len) { // We must always set an output value, even on failure. The output value 0 // means the failure was unrelated to the acceptable response data length. *http_response_data_len = 0; } VLOG(1) << "OCSP try send and receive"; OCSPRequestSession* req = reinterpret_cast(request); // We support blocking mode only. if (pPollDesc) *pPollDesc = NULL; if (req->Started() || req->Finished()) { // We support blocking mode only, so this function shouldn't be called // again when req has stareted or finished. NOTREACHED(); PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation. return SECFailure; } req->Start(); if (!req->Wait() || req->http_response_code() == static_cast(-1)) { // If the response code is -1, the request failed and there is no response. PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation. return SECFailure; } return OCSPSetResponse( req, http_response_code, http_response_content_type, http_response_headers, http_response_data, http_response_data_len); } SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) { VLOG(1) << "OCSP free"; OCSPRequestSession* req = reinterpret_cast(request); req->Cancel(); req->Release(); return SECSuccess; } // Data for GetAlternateOCSPAIAInfo. // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US // // There are two CAs with this name. Their key IDs are listed next. const unsigned char network_solutions_ca_name[] = { 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 }; const unsigned int network_solutions_ca_name_len = base::size(network_solutions_ca_name); // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware. const unsigned char network_solutions_ca_key_id[] = { 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89, 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17 }; const unsigned int network_solutions_ca_key_id_len = base::size(network_solutions_ca_key_id); // This CA is a root CA. It is also cross-certified by // UTN-USERFirst-Hardware. const unsigned char network_solutions_ca_key_id2[] = { 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87, 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c }; const unsigned int network_solutions_ca_key_id2_len = base::size(network_solutions_ca_key_id2); // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are // the key. |ocsp_url| is the value. struct OCSPResponderTableEntry { SECItem issuer; SECItem issuer_key_id; const char *ocsp_url; }; const OCSPResponderTableEntry g_ocsp_responder_table[] = { { { siBuffer, const_cast(network_solutions_ca_name), network_solutions_ca_name_len }, { siBuffer, const_cast(network_solutions_ca_key_id), network_solutions_ca_key_id_len }, "http://ocsp.netsolssl.com" }, { { siBuffer, const_cast(network_solutions_ca_name), network_solutions_ca_name_len }, { siBuffer, const_cast(network_solutions_ca_key_id2), network_solutions_ca_key_id2_len }, "http://ocsp.netsolssl.com" } }; char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) { if (cert && !cert->isRoot && cert->authKeyID) { for (const auto& responder : g_ocsp_responder_table) { if (SECITEM_CompareItem(&responder.issuer, &cert->derIssuer) == SECEqual && SECITEM_CompareItem(&responder.issuer_key_id, &cert->authKeyID->keyID) == SECEqual) { return PORT_Strdup(responder.ocsp_url); } } } return NULL; } } // anonymous namespace void EnsureNSSHttpIOInit() { g_ocsp_nss_initialization.Get(); } void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) { pthread_mutex_lock(&g_request_context_lock); if (request_context) { DCHECK(!g_request_context); } g_request_context = request_context; pthread_mutex_unlock(&g_request_context_lock); if (request_context) { g_ocsp_io_loop.Get().StartUsing(); } else { g_ocsp_io_loop.Get().Shutdown(); } } } // namespace net