| 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 |