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 #ifndef HashStore_h__ 6 #define HashStore_h__ 7 8 #include "Entries.h" 9 #include "ChunkSet.h" 10 11 #include "nsString.h" 12 #include "nsTArray.h" 13 #include "nsIFile.h" 14 #include "nsISupports.h" 15 #include "nsCOMPtr.h" 16 #include "nsClassHashtable.h" 17 #include <string> 18 19 namespace mozilla { 20 namespace safebrowsing { 21 22 // The abstract class of TableUpdateV2 and TableUpdateV4. This 23 // is convenient for passing the TableUpdate* around associated 24 // with v2 and v4 instance. 25 class TableUpdate { 26 public: TableUpdate(const nsACString & aTable)27 TableUpdate(const nsACString& aTable) : mTable(aTable) {} 28 29 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableUpdate); 30 31 // To be overriden. 32 virtual bool Empty() const = 0; 33 34 // Common interfaces. TableName()35 const nsCString& TableName() const { return mTable; } 36 37 template <typename T> Cast(TableUpdate * aThat)38 static T* Cast(TableUpdate* aThat) { 39 return (T::TAG == aThat->Tag() ? reinterpret_cast<T*>(aThat) : nullptr); 40 } 41 template <typename T> Cast(const TableUpdate * aThat)42 static const T* Cast(const TableUpdate* aThat) { 43 return (T::TAG == aThat->Tag() ? reinterpret_cast<const T*>(aThat) 44 : nullptr); 45 } 46 47 protected: 48 virtual ~TableUpdate() = default; 49 50 private: 51 virtual int Tag() const = 0; 52 53 const nsCString mTable; 54 }; 55 56 typedef nsTArray<RefPtr<TableUpdate>> TableUpdateArray; 57 typedef nsTArray<RefPtr<const TableUpdate>> ConstTableUpdateArray; 58 59 // A table update is built from a single update chunk from the server. As the 60 // protocol parser processes each chunk, it constructs a table update with the 61 // new hashes. 62 class TableUpdateV2 : public TableUpdate { 63 public: TableUpdateV2(const nsACString & aTable)64 explicit TableUpdateV2(const nsACString& aTable) : TableUpdate(aTable) {} 65 Empty()66 bool Empty() const override { 67 return mAddChunks.Length() == 0 && mSubChunks.Length() == 0 && 68 mAddExpirations.Length() == 0 && mSubExpirations.Length() == 0 && 69 mAddPrefixes.Length() == 0 && mSubPrefixes.Length() == 0 && 70 mAddCompletes.Length() == 0 && mSubCompletes.Length() == 0 && 71 mMissPrefixes.Length() == 0; 72 } 73 74 // Throughout, uint32_t aChunk refers only to the chunk number. Chunk data is 75 // stored in the Prefix structures. NewAddChunk(uint32_t aChunk)76 MOZ_MUST_USE nsresult NewAddChunk(uint32_t aChunk) { 77 return mAddChunks.Set(aChunk); 78 }; NewSubChunk(uint32_t aChunk)79 MOZ_MUST_USE nsresult NewSubChunk(uint32_t aChunk) { 80 return mSubChunks.Set(aChunk); 81 }; NewAddExpiration(uint32_t aChunk)82 MOZ_MUST_USE nsresult NewAddExpiration(uint32_t aChunk) { 83 return mAddExpirations.Set(aChunk); 84 }; NewSubExpiration(uint32_t aChunk)85 MOZ_MUST_USE nsresult NewSubExpiration(uint32_t aChunk) { 86 return mSubExpirations.Set(aChunk); 87 }; 88 MOZ_MUST_USE nsresult NewAddPrefix(uint32_t aAddChunk, const Prefix& aPrefix); 89 MOZ_MUST_USE nsresult NewSubPrefix(uint32_t aAddChunk, const Prefix& aPrefix, 90 uint32_t aSubChunk); 91 MOZ_MUST_USE nsresult NewAddComplete(uint32_t aChunk, 92 const Completion& aCompletion); 93 MOZ_MUST_USE nsresult NewSubComplete(uint32_t aAddChunk, 94 const Completion& aCompletion, 95 uint32_t aSubChunk); 96 MOZ_MUST_USE nsresult NewMissPrefix(const Prefix& aPrefix); 97 AddChunks()98 const ChunkSet& AddChunks() const { return mAddChunks; } SubChunks()99 const ChunkSet& SubChunks() const { return mSubChunks; } 100 101 // Expirations for chunks. AddExpirations()102 const ChunkSet& AddExpirations() const { return mAddExpirations; } SubExpirations()103 const ChunkSet& SubExpirations() const { return mSubExpirations; } 104 105 // Hashes associated with this chunk. AddPrefixes()106 AddPrefixArray& AddPrefixes() { return mAddPrefixes; } SubPrefixes()107 SubPrefixArray& SubPrefixes() { return mSubPrefixes; } AddCompletes()108 const AddCompleteArray& AddCompletes() const { return mAddCompletes; } AddCompletes()109 AddCompleteArray& AddCompletes() { return mAddCompletes; } SubCompletes()110 SubCompleteArray& SubCompletes() { return mSubCompletes; } 111 112 // Entries that cannot be completed. MissPrefixes()113 const MissPrefixArray& MissPrefixes() const { return mMissPrefixes; } 114 115 // For downcasting. 116 static const int TAG = 2; 117 118 private: 119 // The list of chunk numbers that we have for each of the type of chunks. 120 ChunkSet mAddChunks; 121 ChunkSet mSubChunks; 122 ChunkSet mAddExpirations; 123 ChunkSet mSubExpirations; 124 125 // 4-byte sha256 prefixes. 126 AddPrefixArray mAddPrefixes; 127 SubPrefixArray mSubPrefixes; 128 129 // This is only used by gethash so don't add this to Header. 130 MissPrefixArray mMissPrefixes; 131 132 // 32-byte hashes. 133 AddCompleteArray mAddCompletes; 134 SubCompleteArray mSubCompletes; 135 Tag()136 virtual int Tag() const override { return TAG; } 137 }; 138 139 // Structure for DBService/HashStore/Classifiers to update. 140 // It would contain the prefixes (both fixed and variable length) 141 // for addition and indices to removal. See Bug 1283009. 142 class TableUpdateV4 : public TableUpdate { 143 public: 144 typedef nsTArray<int32_t> RemovalIndiceArray; 145 146 public: TableUpdateV4(const nsACString & aTable)147 explicit TableUpdateV4(const nsACString& aTable) 148 : TableUpdate(aTable), mFullUpdate(false) {} 149 Empty()150 bool Empty() const override { 151 return mPrefixesMap.IsEmpty() && mRemovalIndiceArray.IsEmpty() && 152 mFullHashResponseMap.IsEmpty(); 153 } 154 IsFullUpdate()155 bool IsFullUpdate() const { return mFullUpdate; } Prefixes()156 const PrefixStringMap& Prefixes() const { return mPrefixesMap; } RemovalIndices()157 const RemovalIndiceArray& RemovalIndices() const { 158 return mRemovalIndiceArray; 159 } ClientState()160 const nsACString& ClientState() const { return mClientState; } SHA256()161 const nsACString& SHA256() const { return mSHA256; } FullHashResponse()162 const FullHashResponseMap& FullHashResponse() const { 163 return mFullHashResponseMap; 164 } 165 166 // For downcasting. 167 static const int TAG = 4; 168 SetFullUpdate(bool aIsFullUpdate)169 void SetFullUpdate(bool aIsFullUpdate) { mFullUpdate = aIsFullUpdate; } 170 void NewPrefixes(int32_t aSize, const nsACString& aPrefixes); SetNewClientState(const nsACString & aState)171 void SetNewClientState(const nsACString& aState) { mClientState = aState; } 172 void SetSHA256(const std::string& aSHA256); 173 174 nsresult NewRemovalIndices(const uint32_t* aIndices, size_t aNumOfIndices); 175 nsresult NewFullHashResponse(const Prefix& aPrefix, 176 const CachedFullHashResponse& aResponse); 177 178 // Clear Prefixes & Removal indice. 179 void Clear(); 180 181 private: Tag()182 virtual int Tag() const override { return TAG; } 183 184 bool mFullUpdate; 185 PrefixStringMap mPrefixesMap; 186 RemovalIndiceArray mRemovalIndiceArray; 187 nsCString mClientState; 188 nsCString mSHA256; 189 190 // This is used to store response from fullHashes.find. 191 FullHashResponseMap mFullHashResponseMap; 192 }; 193 194 // There is one hash store per table. 195 class HashStore { 196 public: 197 HashStore(const nsACString& aTableName, const nsACString& aProvider, 198 nsIFile* aRootStoreFile); 199 ~HashStore(); 200 TableName()201 const nsCString& TableName() const { return mTableName; } 202 203 // Version is set to 0 by default, it is only used when we want to open 204 // a specific version of HashStore. Note that the intention of aVersion 205 // is only to pass SanityCheck, reading data from older version should 206 // be handled additionally. 207 nsresult Open(uint32_t aVersion = 0); 208 209 // Add Prefixes/Completes are stored partly in the PrefixSet (contains the 210 // Prefix data organized for fast lookup/low RAM usage) and partly in the 211 // HashStore (Add Chunk numbers - only used for updates, slow retrieval). 212 // AugmentAdds function joins the separate datasets into one complete 213 // prefixes+chunknumbers dataset. 214 nsresult AugmentAdds(const nsTArray<uint32_t>& aPrefixes, 215 const nsTArray<nsCString>& aCompletes); 216 217 ChunkSet& AddChunks(); 218 ChunkSet& SubChunks(); AddPrefixes()219 AddPrefixArray& AddPrefixes() { return mAddPrefixes; } SubPrefixes()220 SubPrefixArray& SubPrefixes() { return mSubPrefixes; } AddCompletes()221 AddCompleteArray& AddCompletes() { return mAddCompletes; } SubCompletes()222 SubCompleteArray& SubCompletes() { return mSubCompletes; } 223 224 // ======= 225 // Updates 226 // ======= 227 // Begin the update process. Reads the store into memory. 228 nsresult BeginUpdate(); 229 230 // Imports the data from a TableUpdate. 231 nsresult ApplyUpdate(RefPtr<TableUpdateV2> aUpdate); 232 233 // Process expired chunks 234 nsresult Expire(); 235 236 // Rebuild the store, Incorporating all the applied updates. 237 nsresult Rebuild(); 238 239 // Write the current state of the store to disk. 240 // If you call between ApplyUpdate() and Rebuild(), you'll 241 // have a mess on your hands. 242 nsresult WriteFile(); 243 244 nsresult ReadCompletionsLegacyV3(AddCompleteArray& aCompletes); 245 246 nsresult Reset(); 247 248 private: 249 nsresult ReadHeader(); 250 nsresult SanityCheck(uint32_t aVersion = 0) const; 251 nsresult CalculateChecksum(nsAutoCString& aChecksum, uint32_t aFileSize, 252 bool aChecksumPresent); 253 nsresult CheckChecksum(uint32_t aFileSize); 254 void UpdateHeader(); 255 256 nsresult ReadCompletions(); 257 nsresult ReadChunkNumbers(); 258 nsresult ReadHashes(); 259 260 nsresult ReadAddPrefixes(); 261 nsresult ReadSubPrefixes(); 262 nsresult ReadAddCompletes(); 263 264 nsresult WriteAddPrefixChunks(nsIOutputStream* aOut); 265 nsresult WriteSubPrefixes(nsIOutputStream* aOut); 266 nsresult WriteAddCompleteChunks(nsIOutputStream* aOut); 267 268 nsresult ProcessSubs(); 269 270 nsresult PrepareForUpdate(); 271 272 // This is used for checking that the database is correct and for figuring out 273 // the number of chunks, etc. to read from disk on restart. 274 struct Header { 275 uint32_t magic; 276 uint32_t version; 277 uint32_t numAddChunks; 278 uint32_t numSubChunks; 279 uint32_t numAddPrefixes; 280 uint32_t numSubPrefixes; 281 uint32_t numAddCompletes; 282 uint32_t numSubCompletes; 283 }; 284 285 Header mHeader; 286 287 // The name of the table (must end in -shavar or -digest256, or evidently 288 // -simple for unittesting. 289 const nsCString mTableName; 290 nsCOMPtr<nsIFile> mStoreDirectory; 291 292 bool mInUpdate; 293 294 nsCOMPtr<nsIInputStream> mInputStream; 295 296 // Chunk numbers, stored as uint32_t arrays. 297 ChunkSet mAddChunks; 298 ChunkSet mSubChunks; 299 300 ChunkSet mAddExpirations; 301 ChunkSet mSubExpirations; 302 303 // Chunk data for shavar tables. See Entries.h for format. 304 AddPrefixArray mAddPrefixes; 305 SubPrefixArray mSubPrefixes; 306 307 // See bug 806422 for background. We must be able to distinguish between 308 // updates from the completion server and updates from the regular server. 309 AddCompleteArray mAddCompletes; 310 SubCompleteArray mSubCompletes; 311 312 uint32_t mFileSize; 313 314 // For gtest to inspect private members. 315 friend class PerProviderDirectoryTestUtils; 316 }; 317 318 } // namespace safebrowsing 319 } // namespace mozilla 320 321 #endif 322