OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // Parse the data returned from the SafeBrowsing v2.1 protocol response. | 5 // Parse the data returned from the SafeBrowsing v2.1 protocol response. |
6 | 6 |
7 #include <stdlib.h> | 7 #include <stdlib.h> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 26 matching lines...) Expand all Loading... |
37 | 37 |
38 //------------------------------------------------------------------------------ | 38 //------------------------------------------------------------------------------ |
39 // SafeBrowsingParser implementation | 39 // SafeBrowsingParser implementation |
40 | 40 |
41 SafeBrowsingProtocolParser::SafeBrowsingProtocolParser() { | 41 SafeBrowsingProtocolParser::SafeBrowsingProtocolParser() { |
42 } | 42 } |
43 | 43 |
44 bool SafeBrowsingProtocolParser::ParseGetHash( | 44 bool SafeBrowsingProtocolParser::ParseGetHash( |
45 const char* chunk_data, | 45 const char* chunk_data, |
46 int chunk_len, | 46 int chunk_len, |
47 const std::string& key, | |
48 bool* re_key, | |
49 std::vector<SBFullHashResult>* full_hashes) { | 47 std::vector<SBFullHashResult>* full_hashes) { |
50 full_hashes->clear(); | 48 full_hashes->clear(); |
51 int length = chunk_len; | 49 int length = chunk_len; |
52 const char* data = chunk_data; | 50 const char* data = chunk_data; |
53 | 51 |
54 int offset; | 52 int offset; |
55 std::string line; | 53 std::string line; |
56 if (!key.empty()) { | |
57 if (!GetLine(data, length, &line)) | |
58 return false; // Error! Bad GetHash result. | |
59 | |
60 if (line == "e:pleaserekey") { | |
61 *re_key = true; | |
62 return true; | |
63 } | |
64 | |
65 offset = static_cast<int>(line.size()) + 1; | |
66 data += offset; | |
67 length -= offset; | |
68 | |
69 if (!safe_browsing_util::VerifyMAC(key, line, data, length)) | |
70 return false; | |
71 } | |
72 | |
73 while (length > 0) { | 54 while (length > 0) { |
74 if (!GetLine(data, length, &line)) | 55 if (!GetLine(data, length, &line)) |
75 return false; | 56 return false; |
76 | 57 |
77 offset = static_cast<int>(line.size()) + 1; | 58 offset = static_cast<int>(line.size()) + 1; |
78 data += offset; | 59 data += offset; |
79 length -= offset; | 60 length -= offset; |
80 | 61 |
81 std::vector<std::string> cmd_parts; | 62 std::vector<std::string> cmd_parts; |
82 base::SplitString(line, ':', &cmd_parts); | 63 base::SplitString(line, ':', &cmd_parts); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 sizeof(SBPrefix) * prefixes.size())); | 99 sizeof(SBPrefix) * prefixes.size())); |
119 for (size_t i = 0; i < prefixes.size(); ++i) { | 100 for (size_t i = 0; i < prefixes.size(); ++i) { |
120 request->append(reinterpret_cast<const char*>(&prefixes[i]), | 101 request->append(reinterpret_cast<const char*>(&prefixes[i]), |
121 sizeof(SBPrefix)); | 102 sizeof(SBPrefix)); |
122 } | 103 } |
123 } | 104 } |
124 | 105 |
125 bool SafeBrowsingProtocolParser::ParseUpdate( | 106 bool SafeBrowsingProtocolParser::ParseUpdate( |
126 const char* chunk_data, | 107 const char* chunk_data, |
127 int chunk_len, | 108 int chunk_len, |
128 const std::string& key, | |
129 int* next_update_sec, | 109 int* next_update_sec, |
130 bool* re_key, | |
131 bool* reset, | 110 bool* reset, |
132 std::vector<SBChunkDelete>* deletes, | 111 std::vector<SBChunkDelete>* deletes, |
133 std::vector<ChunkUrl>* chunk_urls) { | 112 std::vector<ChunkUrl>* chunk_urls) { |
134 DCHECK(next_update_sec); | 113 DCHECK(next_update_sec); |
135 DCHECK(deletes); | 114 DCHECK(deletes); |
136 DCHECK(chunk_urls); | 115 DCHECK(chunk_urls); |
137 | 116 |
138 int length = chunk_len; | 117 int length = chunk_len; |
139 const char* data = chunk_data; | 118 const char* data = chunk_data; |
140 | 119 |
141 // Populated below. | 120 // Populated below. |
142 std::string list_name; | 121 std::string list_name; |
143 | 122 |
144 // If we requested the MAC, the response must start with a MAC command. | |
145 // This test ensures it is present, the value will be verified in the | |
146 // switch statement below. | |
147 if (!key.empty() && (length < 1 || data[0] != 'm')) | |
148 return false; | |
149 | |
150 while (length > 0) { | 123 while (length > 0) { |
151 std::string cmd_line; | 124 std::string cmd_line; |
152 if (!GetLine(data, length, &cmd_line)) | 125 if (!GetLine(data, length, &cmd_line)) |
153 return false; // Error: bad list format! | 126 return false; // Error: bad list format! |
154 | 127 |
155 std::vector<std::string> cmd_parts; | 128 std::vector<std::string> cmd_parts; |
156 base::SplitString(cmd_line, ':', &cmd_parts); | 129 base::SplitString(cmd_line, ':', &cmd_parts); |
157 if (cmd_parts.empty()) | 130 if (cmd_parts.empty()) |
158 return false; | 131 return false; |
159 const std::string& command = cmd_parts[0]; | 132 const std::string& command = cmd_parts[0]; |
(...skipping 17 matching lines...) Expand all Loading... |
177 if (command.size() != 2 || command[1] != 'd' || list_name.empty()) | 150 if (command.size() != 2 || command[1] != 'd' || list_name.empty()) |
178 return false; | 151 return false; |
179 SBChunkDelete chunk_delete; | 152 SBChunkDelete chunk_delete; |
180 chunk_delete.is_sub_del = command[0] == 's'; | 153 chunk_delete.is_sub_del = command[0] == 's'; |
181 StringToRanges(cmd_parts[1], &chunk_delete.chunk_del); | 154 StringToRanges(cmd_parts[1], &chunk_delete.chunk_del); |
182 chunk_delete.list_name = list_name; | 155 chunk_delete.list_name = list_name; |
183 deletes->push_back(chunk_delete); | 156 deletes->push_back(chunk_delete); |
184 break; | 157 break; |
185 } | 158 } |
186 | 159 |
187 case 'e': | |
188 if (cmd_parts[1] != "pleaserekey") | |
189 return false; | |
190 *re_key = true; | |
191 break; | |
192 | |
193 case 'i': | 160 case 'i': |
194 // The line providing the name of the list (i.e. 'goog-phish-shavar'). | 161 // The line providing the name of the list (i.e. 'goog-phish-shavar'). |
195 list_name = cmd_parts[1]; | 162 list_name = cmd_parts[1]; |
196 break; | 163 break; |
197 | 164 |
198 case 'm': | |
199 // Verify that the MAC of the remainer of this chunk is what we expect. | |
200 if (!key.empty() && | |
201 !safe_browsing_util::VerifyMAC(key, cmd_parts[1], data, length)) | |
202 return false; | |
203 break; | |
204 | |
205 case 'n': | 165 case 'n': |
206 // The line providing the next earliest time (in seconds) to re-query. | 166 // The line providing the next earliest time (in seconds) to re-query. |
207 *next_update_sec = atoi(cmd_parts[1].c_str()); | 167 *next_update_sec = atoi(cmd_parts[1].c_str()); |
208 break; | 168 break; |
209 | 169 |
210 case 'u': { | 170 case 'u': { |
211 // The redirect command is of the form: u:<url>,<mac> where <url> can | |
212 // contain multiple colons, commas or any valid URL characters. We scan | |
213 // backwards in the string looking for the first ',' we encounter and | |
214 // assume that everything before that is the URL and everything after | |
215 // is the MAC (if the MAC was requested). | |
216 std::string mac; | |
217 std::string redirect_url(cmd_line, 2); // Skip the initial "u:". | |
218 if (!key.empty()) { | |
219 std::string::size_type mac_pos = redirect_url.rfind(','); | |
220 if (mac_pos == std::string::npos) | |
221 return false; | |
222 mac = redirect_url.substr(mac_pos + 1); | |
223 redirect_url = redirect_url.substr(0, mac_pos); | |
224 } | |
225 | |
226 ChunkUrl chunk_url; | 171 ChunkUrl chunk_url; |
227 chunk_url.url = redirect_url; | 172 chunk_url.url = cmd_line.substr(2); // Skip the initial "u:". |
228 chunk_url.list_name = list_name; | 173 chunk_url.list_name = list_name; |
229 if (!key.empty()) | |
230 chunk_url.mac = mac; | |
231 chunk_urls->push_back(chunk_url); | 174 chunk_urls->push_back(chunk_url); |
232 break; | 175 break; |
233 } | 176 } |
234 | 177 |
235 case 'r': | 178 case 'r': |
236 if (cmd_parts[1] != "pleasereset") | 179 if (cmd_parts[1] != "pleasereset") |
237 return false; | 180 return false; |
238 *reset = true; | 181 *reset = true; |
239 break; | 182 break; |
240 | 183 |
241 default: | 184 default: |
242 // According to the spec, we ignore commands we don't understand. | 185 // According to the spec, we ignore commands we don't understand. |
243 break; | 186 break; |
244 } | 187 } |
245 } | 188 } |
246 | 189 |
247 return true; | 190 return true; |
248 } | 191 } |
249 | 192 |
250 bool SafeBrowsingProtocolParser::ParseChunk(const std::string& list_name, | 193 bool SafeBrowsingProtocolParser::ParseChunk(const std::string& list_name, |
251 const char* data, | 194 const char* data, |
252 int length, | 195 int length, |
253 const std::string& key, | |
254 const std::string& mac, | |
255 bool* re_key, | |
256 SBChunkList* chunks) { | 196 SBChunkList* chunks) { |
257 int remaining = length; | 197 int remaining = length; |
258 const char* chunk_data = data; | 198 const char* chunk_data = data; |
259 | 199 |
260 if (!key.empty() && | |
261 !safe_browsing_util::VerifyMAC(key, mac, data, length)) { | |
262 return false; | |
263 } | |
264 | |
265 while (remaining > 0) { | 200 while (remaining > 0) { |
266 std::string cmd_line; | 201 std::string cmd_line; |
267 if (!GetLine(chunk_data, length, &cmd_line)) | 202 if (!GetLine(chunk_data, length, &cmd_line)) |
268 return false; // Error: bad chunk format! | 203 return false; // Error: bad chunk format! |
269 | 204 |
270 const int line_len = static_cast<int>(cmd_line.length()) + 1; | 205 const int line_len = static_cast<int>(cmd_line.length()) + 1; |
271 chunk_data += line_len; | 206 chunk_data += line_len; |
272 remaining -= line_len; | 207 remaining -= line_len; |
273 std::vector<std::string> cmd_parts; | 208 std::vector<std::string> cmd_parts; |
274 base::SplitString(cmd_line, ':', &cmd_parts); | 209 base::SplitString(cmd_line, ':', &cmd_parts); |
275 | |
276 // Handle a possible re-key command. | |
277 if (cmd_parts.size() != 4) { | 210 if (cmd_parts.size() != 4) { |
278 if (cmd_parts.size() == 2 && | |
279 cmd_parts[0] == "e" && | |
280 cmd_parts[1] == "pleaserekey") { | |
281 *re_key = true; | |
282 continue; | |
283 } | |
284 return false; | 211 return false; |
285 } | 212 } |
286 | 213 |
287 // Process the chunk data. | 214 // Process the chunk data. |
288 const int chunk_number = atoi(cmd_parts[1].c_str()); | 215 const int chunk_number = atoi(cmd_parts[1].c_str()); |
289 const int hash_len = atoi(cmd_parts[2].c_str()); | 216 const int hash_len = atoi(cmd_parts[2].c_str()); |
290 if (hash_len != sizeof(SBPrefix) && hash_len != sizeof(SBFullHash)) { | 217 if (hash_len != sizeof(SBPrefix) && hash_len != sizeof(SBFullHash)) { |
291 VLOG(1) << "ParseChunk got unknown hashlen " << hash_len; | 218 VLOG(1) << "ParseChunk got unknown hashlen " << hash_len; |
292 return false; | 219 return false; |
293 } | 220 } |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
478 DCHECK_EQ(hash_len, (int)sizeof(SBFullHash)); | 405 DCHECK_EQ(hash_len, (int)sizeof(SBFullHash)); |
479 entry->SetFullHashAt(i, *reinterpret_cast<const SBFullHash*>(*data)); | 406 entry->SetFullHashAt(i, *reinterpret_cast<const SBFullHash*>(*data)); |
480 } | 407 } |
481 *data += hash_len; | 408 *data += hash_len; |
482 *remaining -= hash_len; | 409 *remaining -= hash_len; |
483 DCHECK_GE(*remaining, 0); | 410 DCHECK_GE(*remaining, 0); |
484 } | 411 } |
485 | 412 |
486 return true; | 413 return true; |
487 } | 414 } |
488 | |
489 bool SafeBrowsingProtocolParser::ParseNewKey(const char* chunk_data, | |
490 int chunk_length, | |
491 std::string* client_key, | |
492 std::string* wrapped_key) { | |
493 DCHECK(client_key && wrapped_key); | |
494 client_key->clear(); | |
495 wrapped_key->clear(); | |
496 | |
497 const char* data = chunk_data; | |
498 int remaining = chunk_length; | |
499 | |
500 while (remaining > 0) { | |
501 std::string line; | |
502 if (!GetLine(data, remaining, &line)) | |
503 return false; | |
504 | |
505 std::vector<std::string> cmd_parts; | |
506 base::SplitString(line, ':', &cmd_parts); | |
507 if (cmd_parts.size() != 3) | |
508 return false; | |
509 | |
510 if (static_cast<int>(cmd_parts[2].size()) != atoi(cmd_parts[1].c_str())) | |
511 return false; | |
512 | |
513 if (cmd_parts[0] == "clientkey") { | |
514 client_key->assign(cmd_parts[2]); | |
515 } else if (cmd_parts[0] == "wrappedkey") { | |
516 wrapped_key->assign(cmd_parts[2]); | |
517 } else { | |
518 return false; | |
519 } | |
520 | |
521 data += line.size() + 1; | |
522 remaining -= static_cast<int>(line.size()) + 1; | |
523 } | |
524 | |
525 if (client_key->empty() || wrapped_key->empty()) | |
526 return false; | |
527 | |
528 return true; | |
529 } | |
OLD | NEW |