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