bool OCSPCache::Get(const CertID& aCertID, PRErrorCode& aErrorCode, PRTime& aValidThrough) { MutexAutoLock lock(mMutex); size_t index; if (!FindInternal(aCertID, index, lock)) { LogWithCertID("OCSPCache::Get(%p) not in cache", aCertID); return false; } LogWithCertID("OCSPCache::Get(%p) in cache", aCertID); aErrorCode = mEntries[index]->mErrorCode; aValidThrough = mEntries[index]->mValidThrough; MakeMostRecentlyUsed(index, lock); return true; }
/** Search for a node by directory path, start from children of aNodeToStart but do not include aNodeToStart. @param aPath the path as the key to search in the tree @param aNodeToStart the node whose children to start with @param aNodeFound in return, the node found @param aDirPos the location of the directory @return KErrNone if a node found KErrNotFound if no node is found */ TInt CLeafDirTree::DoSearch(const TDesC& aPath, CLeafDirTreeNode* aNodeToStart, CLeafDirTreeNode*& aNodeFound, TLeafDirData& aLeafDirData) { RPointerArray<CLeafDirTreeNode> currentLevel = aNodeToStart->Children(); TInt currentPos = currentLevel.Count() - 1; // Current path in search TPtrC currentPath; currentPath.Set(aPath); while (currentLevel.Count() > 0 && currentPos >= 0) { CLeafDirTreeNode* currentNode = currentLevel[currentPos]; TPtrC currentNodePath; currentNodePath.Set(currentNode->Path()); TInt foundPos = currentPath.FindF(currentNodePath); // If current child's path is part of the searching path, // go to next level // E.g.: current child's path = "1\2\3\", searching path = "1\2\3\5\". if (foundPos == 0 && currentNodePath.Length() < currentPath.Length()) { currentPath.Set(currentPath.Mid(currentNodePath.Length())); currentLevel = currentNode->Children(); currentPos = currentLevel.Count() - 1; continue; } // If current child's path matches current searching path, // check the node type. else if (foundPos == 0 && currentNodePath.Length() == currentPath.Length()) { if (currentNode->IsPureIntermediary()) // If found is 'pure intermediary', it is not cached. { return KErrNotFound; } // Otherwise, we have got a cache hit! MakeMostRecentlyUsed(currentNode); aNodeFound = currentNode; aLeafDirData = currentNode->LeafDirData(); return KErrNone; } // else, go through current level currentPos--; } // If there is no child or we have not found any matching node, // return KErrNotFound return KErrNotFound; }
bool OCSPCache::Get(const CERTCertificate* aCert, const CERTCertificate* aIssuerCert, PRErrorCode& aErrorCode, PRTime& aValidThrough) { PR_ASSERT(aCert); PR_ASSERT(aIssuerCert); MutexAutoLock lock(mMutex); size_t index; if (!FindInternal(aCert, aIssuerCert, index, lock)) { LogWithCerts("OCSPCache::Get(%s, %s) not in cache", aCert, aIssuerCert); return false; } LogWithCerts("OCSPCache::Get(%s, %s) in cache", aCert, aIssuerCert); aErrorCode = mEntries[index]->mErrorCode; aValidThrough = mEntries[index]->mValidThrough; MakeMostRecentlyUsed(index, lock); return true; }
Result OCSPCache::Put(const CertID& aCertID, Result aResult, Time aThisUpdate, Time aValidThrough) { MutexAutoLock lock(mMutex); size_t index; if (FindInternal(aCertID, index, lock)) { // Never replace an entry indicating a revoked certificate. if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache as revoked - " "not replacing", aCertID); MakeMostRecentlyUsed(index, lock); return Success; } // Never replace a newer entry with an older one unless the older entry // indicates a revoked certificate, which we want to remember. if (mEntries[index]->mThisUpdate > aThisUpdate && aResult != Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache with more recent " "validity - not replacing", aCertID); MakeMostRecentlyUsed(index, lock); return Success; } // Only known good responses or responses indicating an unknown // or revoked certificate should replace previously known responses. if (aResult != Success && aResult != Result::ERROR_OCSP_UNKNOWN_CERT && aResult != Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache - not replacing " "with less important status", aCertID); MakeMostRecentlyUsed(index, lock); return Success; } LogWithCertID("OCSPCache::Put(%p) already in cache - replacing", aCertID); mEntries[index]->mResult = aResult; mEntries[index]->mThisUpdate = aThisUpdate; mEntries[index]->mValidThrough = aValidThrough; MakeMostRecentlyUsed(index, lock); return Success; } if (mEntries.length() == MaxEntries) { LogWithCertID("OCSPCache::Put(%p) too full - evicting an entry", aCertID); for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end(); toEvict++) { // Never evict an entry that indicates a revoked or unknokwn certificate, // because revoked responses are more security-critical to remember. if ((*toEvict)->mResult != Result::ERROR_REVOKED_CERTIFICATE && (*toEvict)->mResult != Result::ERROR_OCSP_UNKNOWN_CERT) { delete *toEvict; mEntries.erase(toEvict); break; } } // Well, we tried, but apparently everything is revoked or unknown. // We don't want to remove a cached revoked or unknown response. If we're // trying to insert a good response, we can just return "successfully" // without doing so. This means we'll lose some speed, but it's not a // security issue. If we're trying to insert a revoked or unknown response, // we can't. We should return with an error that causes the current // verification to fail. if (mEntries.length() == MaxEntries) { return aResult; } } Entry* newEntry = new (std::nothrow) Entry(aResult, aThisUpdate, aValidThrough); // Normally we don't have to do this in Gecko, because OOM is fatal. // However, if we want to embed this in another project, OOM might not // be fatal, so handle this case. if (!newEntry) { return Result::FATAL_ERROR_NO_MEMORY; } Result rv = newEntry->Init(aCertID); if (rv != Success) { delete newEntry; return rv; } if (!mEntries.append(newEntry)) { delete newEntry; return Result::FATAL_ERROR_NO_MEMORY; } LogWithCertID("OCSPCache::Put(%p) added to cache", aCertID); return Success; }
/** Implementation of the insertion algorithm @param aNodeToStart the node whose children to start with @param aPath the path of the new node to be inserted @param aDirPos the position of the new node to be inserted @param aNodeInserted in return, the node that has been successfully inserted */ void CLeafDirTree::DoInsertL(CLeafDirTreeNode* aNodeToStart, const TDesC& aPath, const TLeafDirData& aLeafDirData, CLeafDirTreeNode*& aNodeInserted) { CLeafDirTreeNode* currentParent = aNodeToStart; TInt foundPos = 0; RPointerArray<CLeafDirTreeNode> currentLevel = aNodeToStart->Children(); TInt currentPos = currentLevel.Count() - 1; TPtrC currentPath; currentPath.Set(aPath); while (currentLevel.Count() > 0 && currentPos >= 0) { CLeafDirTreeNode* currentNode = currentLevel[currentPos]; TPtrC currentNodePath; currentNodePath.Set(currentNode->Path()); // If current node is contained by aPath. // E.g.: current node = "1\2\3\", currentPath = "1\2\3\5\" // In this case, we need to go to next level, // discard logged position (currentPos) in this level as we don't need to come back. foundPos = currentPath.FindF(currentNodePath); if (foundPos == 0 && currentNodePath.Length() < currentPath.Length()) { currentParent = currentNode; currentLevel = currentNode->Children(); currentPos = currentLevel.Count() - 1; currentPath.Set(currentPath.Mid(currentNodePath.Length())); continue; } // If current node's path contains aPath // E.g.: current node = "1\2\3\4\", currentPath = "1\2\3\" // We need to split current node to two nodes and return. foundPos = currentNodePath.FindF(currentPath); if (foundPos == 0 && currentNodePath.Length() > currentPath.Length()) { CLeafDirTreeNode* newNode = CLeafDirTreeNode::NewL(this, currentPath, aLeafDirData, CLeafDirTreeNode::ELeafIntermediary); currentParent->MakeItChildL(newNode); TPtrC restPath; restPath.Set(currentNodePath.Mid(currentPath.Length())); currentNode->SetPathL(restPath); currentParent->RemoveChild(currentNode); newNode->MakeItChildL(currentNode); AddOntoLruL(newNode); aNodeInserted = newNode; return; } // If current node's path equals aPath, // change the node type if it is necessary if (foundPos == 0 && currentNodePath.Length() == currentPath.Length()) { // Check node type, if already cached, update Lru list and return. if (currentNode->IsLeaf() || currentNode->IsLeafIntermediary()) { currentNode->SetLeafDirData(aLeafDirData); aNodeInserted = currentNode; MakeMostRecentlyUsed(currentNode); return; } // If it has not been cached yet, i.e., it is a 'pure intermediary' node, // cache the node and put it onto Lru list else if(currentNode->IsPureIntermediary()) { currentNode->SetLeafDirData(aLeafDirData); currentNode->SetType(CLeafDirTreeNode::ELeafIntermediary); AddOntoLruL(currentNode); aNodeInserted = currentNode; return; } } // If none of above is the case (i.e. haven't found exact match or paths // are not contained by each other), we need to find the first common part // between each child and aPath to share path data. foundPos = FindLongestCommonPath(currentNodePath, currentPath); // If a common part of path is found, we need to create a pure intermediary node to share // the common part of path data, and create a new leaf node for the target path. if (foundPos > 0) { TPtrC commonPath; commonPath.Set(currentNodePath.Left(foundPos + 1)); currentNodePath.Set(currentNodePath.Mid(foundPos + 1)); TPtrC newLeafPath; newLeafPath.Set(currentPath.Mid(foundPos + 1)); // Add new pureintermediary node, set it as child of current parent TLeafDirData dummyPos(0); CLeafDirTreeNode* newPureIntermediaryNode = CLeafDirTreeNode::NewL(this, commonPath, dummyPos, CLeafDirTreeNode::EPureIntermediary); currentParent->MakeItChildL(newPureIntermediaryNode); // Remove current child from aNodeToStart, do not need to change // node type of aNodeToStart currentParent->RemoveChild(currentNode); // Modify current pathData, make it child of new node newPureIntermediaryNode->MakeItChildL(currentNode); currentNode->SetPathL(currentNodePath); // Add new leaf node as a child of the new pure intermediary node CLeafDirTreeNode* newLeafNode = CLeafDirTreeNode::NewL(this, newLeafPath, aLeafDirData, CLeafDirTreeNode::ELeaf); newPureIntermediaryNode->MakeItChildL(newLeafNode); aNodeInserted = newLeafNode; AddOntoLruL(newLeafNode); return; } // Otherwise, move on within this level. currentPos--; } // No match case found, add a new node straight on at current level CLeafDirTreeNode* newNode = CLeafDirTreeNode::NewL(this, currentPath, aLeafDirData, CLeafDirTreeNode::ELeaf); if (currentParent->IsLeaf()) // might be the root node { currentParent->SetType(CLeafDirTreeNode::ELeafIntermediary); } currentParent->MakeItChildL(newNode); aNodeInserted = newNode; AddOntoLruL(newNode); }