1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef ProtocolParser_h__
7 #define ProtocolParser_h__
8 
9 #include "HashStore.h"
10 #include "nsICryptoHMAC.h"
11 #include "safebrowsing.pb.h"
12 
13 namespace mozilla {
14 namespace safebrowsing {
15 
16 /**
17  * Abstract base class for parsing update data in multiple formats.
18  */
19 class ProtocolParser {
20 public:
21   struct ForwardedUpdate {
22     nsCString table;
23     nsCString url;
24   };
25 
26   ProtocolParser();
27   virtual ~ProtocolParser();
28 
Status()29   nsresult Status() const { return mUpdateStatus; }
30 
31   nsresult Init(nsICryptoHash* aHasher);
32 
33 #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
GetRawTableUpdates()34   nsCString GetRawTableUpdates() const { return mPending; }
35 #endif
36 
37   virtual void SetCurrentTable(const nsACString& aTable) = 0;
38 
SetRequestedTables(const nsTArray<nsCString> & aRequestTables)39   void SetRequestedTables(const nsTArray<nsCString>& aRequestTables)
40   {
41     mRequestedTables = aRequestTables;
42   }
43 
44   nsresult Begin();
45   virtual nsresult AppendStream(const nsACString& aData) = 0;
46 
UpdateWaitSec()47   uint32_t UpdateWaitSec() { return mUpdateWaitSec; }
48 
49   // Notify that the inbound data is ready for parsing if progressive
50   // parsing is not supported, for example in V4.
51   virtual void End() = 0;
52 
53   // Forget the table updates that were created by this pass.  It
54   // becomes the caller's responsibility to free them.  This is shitty.
55   TableUpdate *GetTableUpdate(const nsACString& aTable);
ForgetTableUpdates()56   void ForgetTableUpdates() { mTableUpdates.Clear(); }
GetTableUpdates()57   nsTArray<TableUpdate*> &GetTableUpdates() { return mTableUpdates; }
58 
59   // These are only meaningful to V2. Since they were originally public,
60   // moving them to ProtocolParserV2 requires a dymamic cast in the call
61   // sites. As a result, we will leave them until we remove support
62   // for V2 entirely..
Forwards()63   virtual const nsTArray<ForwardedUpdate> &Forwards() const { return mForwards; }
ResetRequested()64   virtual bool ResetRequested() { return false; }
65 
66 protected:
67   virtual TableUpdate* CreateTableUpdate(const nsACString& aTableName) const = 0;
68 
69   nsCString mPending;
70   nsresult mUpdateStatus;
71 
72   // Keep track of updates to apply before passing them to the DBServiceWorkers.
73   nsTArray<TableUpdate*> mTableUpdates;
74 
75   nsTArray<ForwardedUpdate> mForwards;
76   nsCOMPtr<nsICryptoHash> mCryptoHash;
77 
78   // The table names that were requested from the client.
79   nsTArray<nsCString> mRequestedTables;
80 
81   // How long we should wait until the next update.
82   uint32_t mUpdateWaitSec;
83 
84 private:
85   void CleanupUpdates();
86 };
87 
88 /**
89  * Helpers to parse the "shavar", "digest256" and "simple" list formats.
90  */
91 class ProtocolParserV2 final : public ProtocolParser {
92 public:
93   ProtocolParserV2();
94   virtual ~ProtocolParserV2();
95 
96   virtual void SetCurrentTable(const nsACString& aTable) override;
97   virtual nsresult AppendStream(const nsACString& aData) override;
98   virtual void End() override;
99 
100   // Update information.
Forwards()101   virtual const nsTArray<ForwardedUpdate> &Forwards() const override { return mForwards; }
ResetRequested()102   virtual bool ResetRequested() override { return mResetRequested; }
103 
104 private:
105   virtual TableUpdate* CreateTableUpdate(const nsACString& aTableName) const override;
106 
107   nsresult ProcessControl(bool* aDone);
108   nsresult ProcessExpirations(const nsCString& aLine);
109   nsresult ProcessChunkControl(const nsCString& aLine);
110   nsresult ProcessForward(const nsCString& aLine);
111   nsresult AddForward(const nsACString& aUrl);
112   nsresult ProcessChunk(bool* done);
113   // Remove this, it's only used for testing
114   nsresult ProcessPlaintextChunk(const nsACString& aChunk);
115   nsresult ProcessShaChunk(const nsACString& aChunk);
116   nsresult ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries,
117                           const nsACString& aChunk, uint32_t* aStart);
118   nsresult ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries,
119                           const nsACString& aChunk, uint32_t* aStart);
120   nsresult ProcessHostAddComplete(uint8_t aNumEntries, const nsACString& aChunk,
121                                   uint32_t *aStart);
122   nsresult ProcessHostSubComplete(uint8_t numEntries, const nsACString& aChunk,
123                                   uint32_t* start);
124   // Digest chunks are very similar to shavar chunks, except digest chunks
125   // always contain the full hash, so there is no need for chunk data to
126   // contain prefix sizes.
127   nsresult ProcessDigestChunk(const nsACString& aChunk);
128   nsresult ProcessDigestAdd(const nsACString& aChunk);
129   nsresult ProcessDigestSub(const nsACString& aChunk);
130   bool NextLine(nsACString& aLine);
131 
132   enum ParserState {
133     PROTOCOL_STATE_CONTROL,
134     PROTOCOL_STATE_CHUNK
135   };
136   ParserState mState;
137 
138   enum ChunkType {
139     // Types for shavar tables.
140     CHUNK_ADD,
141     CHUNK_SUB,
142     // Types for digest256 tables. digest256 tables differ in format from
143     // shavar tables since they only contain complete hashes.
144     CHUNK_ADD_DIGEST,
145     CHUNK_SUB_DIGEST
146   };
147 
148   struct ChunkState {
149     ChunkType type;
150     uint32_t num;
151     uint32_t hashSize;
152     uint32_t length;
ClearChunkState153     void Clear() { num = 0; hashSize = 0; length = 0; }
154   };
155   ChunkState mChunkState;
156 
157   bool mResetRequested;
158 
159   // Updates to apply to the current table being parsed.
160   TableUpdateV2 *mTableUpdate;
161 };
162 
163 // Helpers to parse the "proto" list format.
164 class ProtocolParserProtobuf final : public ProtocolParser {
165 public:
166   typedef FetchThreatListUpdatesResponse_ListUpdateResponse ListUpdateResponse;
167   typedef google::protobuf::RepeatedPtrField<ThreatEntrySet> ThreatEntrySetList;
168 
169 public:
170   ProtocolParserProtobuf();
171 
172   virtual void SetCurrentTable(const nsACString& aTable) override;
173   virtual nsresult AppendStream(const nsACString& aData) override;
174   virtual void End() override;
175 
176 private:
177   virtual ~ProtocolParserProtobuf();
178 
179   virtual TableUpdate* CreateTableUpdate(const nsACString& aTableName) const override;
180 
181   // For parsing update info.
182   nsresult ProcessOneResponse(const ListUpdateResponse& aResponse);
183 
184   nsresult ProcessAdditionOrRemoval(TableUpdateV4& aTableUpdate,
185                                     const ThreatEntrySetList& aUpdate,
186                                     bool aIsAddition);
187 
188   nsresult ProcessRawAddition(TableUpdateV4& aTableUpdate,
189                               const ThreatEntrySet& aAddition);
190 
191   nsresult ProcessRawRemoval(TableUpdateV4& aTableUpdate,
192                              const ThreatEntrySet& aRemoval);
193 
194   nsresult ProcessEncodedAddition(TableUpdateV4& aTableUpdate,
195                                   const ThreatEntrySet& aAddition);
196 
197   nsresult ProcessEncodedRemoval(TableUpdateV4& aTableUpdate,
198                                  const ThreatEntrySet& aRemoval);
199 };
200 
201 } // namespace safebrowsing
202 } // namespace mozilla
203 
204 #endif
205