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 3 // found in the LICENSE file. 4 5 // Detecting mime types is a tricky business because we need to balance 6 // compatibility concerns with security issues. Here is a survey of how other 7 // browsers behave and then a description of how we intend to behave. 8 // 9 // HTML payload, no Content-Type header: 10 // * IE 7: Render as HTML 11 // * Firefox 2: Render as HTML 12 // * Safari 3: Render as HTML 13 // * Opera 9: Render as HTML 14 // 15 // Here the choice seems clear: 16 // => Chrome: Render as HTML 17 // 18 // HTML payload, Content-Type: "text/plain": 19 // * IE 7: Render as HTML 20 // * Firefox 2: Render as text 21 // * Safari 3: Render as text (Note: Safari will Render as HTML if the URL 22 // has an HTML extension) 23 // * Opera 9: Render as text 24 // 25 // Here we choose to follow the majority (and break some compatibility with IE). 26 // Many folks dislike IE's behavior here. 27 // => Chrome: Render as text 28 // We generalize this as follows. If the Content-Type header is text/plain 29 // we won't detect dangerous mime types (those that can execute script). 30 // 31 // HTML payload, Content-Type: "application/octet-stream": 32 // * IE 7: Render as HTML 33 // * Firefox 2: Download as application/octet-stream 34 // * Safari 3: Render as HTML 35 // * Opera 9: Render as HTML 36 // 37 // We follow Firefox. 38 // => Chrome: Download as application/octet-stream 39 // One factor in this decision is that IIS 4 and 5 will send 40 // application/octet-stream for .xhtml files (because they don't recognize 41 // the extension). We did some experiments and it looks like this doesn't occur 42 // very often on the web. We choose the more secure option. 43 // 44 // GIF payload, no Content-Type header: 45 // * IE 7: Render as GIF 46 // * Firefox 2: Render as GIF 47 // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the 48 // URL has an GIF extension) 49 // * Opera 9: Render as GIF 50 // 51 // The choice is clear. 52 // => Chrome: Render as GIF 53 // Once we decide to render HTML without a Content-Type header, there isn't much 54 // reason not to render GIFs. 55 // 56 // GIF payload, Content-Type: "text/plain": 57 // * IE 7: Render as GIF 58 // * Firefox 2: Download as application/octet-stream (Note: Firefox will 59 // Download as GIF if the URL has an GIF extension) 60 // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the 61 // URL has an GIF extension) 62 // * Opera 9: Render as GIF 63 // 64 // Displaying as text/plain makes little sense as the content will look like 65 // gibberish. Here, we could change our minds and download. 66 // => Chrome: Render as GIF 67 // 68 // GIF payload, Content-Type: "application/octet-stream": 69 // * IE 7: Render as GIF 70 // * Firefox 2: Download as application/octet-stream (Note: Firefox will 71 // Download as GIF if the URL has an GIF extension) 72 // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the 73 // URL has an GIF extension) 74 // * Opera 9: Render as GIF 75 // 76 // We used to render as GIF here, but the problem is that some sites want to 77 // trigger downloads by sending application/octet-stream (even though they 78 // should be sending Content-Disposition: attachment). Although it is safe 79 // to render as GIF from a security perspective, we actually get better 80 // compatibility if we don't sniff from application/octet stream at all. 81 // => Chrome: Download as application/octet-stream 82 // 83 // XHTML payload, Content-Type: "text/xml": 84 // * IE 7: Render as XML 85 // * Firefox 2: Render as HTML 86 // * Safari 3: Render as HTML 87 // * Opera 9: Render as HTML 88 // The layout tests rely on us rendering this as HTML. 89 // But we're conservative in XHTML detection, as this runs afoul of the 90 // "don't detect dangerous mime types" rule. 91 // 92 // Note that our definition of HTML payload is much stricter than IE's 93 // definition and roughly the same as Firefox's definition. 94 95 #include <string> 96 97 #include "net/base/mime_sniffer.h" 98 99 #include "base/basictypes.h" 100 #include "base/logging.h" 101 #include "base/metrics/histogram.h" 102 #include "base/strings/string_util.h" 103 #include "net/base/mime_util.h" 104 #include "url/gurl.h" 105 106 namespace net { 107 108 // The number of content bytes we need to use all our magic numbers. Feel free 109 // to increase this number if you add a longer magic number. 110 static const size_t kBytesRequiredForMagic = 42; 111 112 struct MagicNumber { 113 const char* mime_type; 114 const char* magic; 115 size_t magic_len; 116 bool is_string; 117 const char* mask; // if set, must have same length as |magic| 118 }; 119 120 #define MAGIC_NUMBER(mime_type, magic) \ 121 { (mime_type), (magic), sizeof(magic)-1, false, NULL }, 122 123 template <int MagicSize, int MaskSize> 124 class VerifySizes { 125 COMPILE_ASSERT(MagicSize == MaskSize, sizes_must_be_equal); 126 public: 127 enum { SIZES = MagicSize }; 128 }; 129 130 #define verified_sizeof(magic, mask) \ 131 VerifySizes<sizeof(magic), sizeof(mask)>::SIZES 132 133 #define MAGIC_MASK(mime_type, magic, mask) \ 134 { (mime_type), (magic), verified_sizeof(magic, mask)-1, false, (mask) }, 135 136 // Magic strings are case insensitive and must not include '\0' characters 137 #define MAGIC_STRING(mime_type, magic) \ 138 { (mime_type), (magic), sizeof(magic)-1, true, NULL }, 139 140 static const MagicNumber kMagicNumbers[] = { 141 // Source: HTML 5 specification 142 MAGIC_NUMBER("application/pdf", "%PDF-") 143 MAGIC_NUMBER("application/postscript", "%!PS-Adobe-") 144 MAGIC_NUMBER("image/gif", "GIF87a") 145 MAGIC_NUMBER("image/gif", "GIF89a") 146 MAGIC_NUMBER("image/png", "\x89" "PNG\x0D\x0A\x1A\x0A") 147 MAGIC_NUMBER("image/jpeg", "\xFF\xD8\xFF") 148 MAGIC_NUMBER("image/bmp", "BM") 149 // Source: Mozilla 150 MAGIC_NUMBER("text/plain", "#!") // Script 151 MAGIC_NUMBER("text/plain", "%!") // Script, similar to PS 152 MAGIC_NUMBER("text/plain", "From") 153 MAGIC_NUMBER("text/plain", ">From") 154 // Chrome specific 155 MAGIC_NUMBER("application/x-gzip", "\x1F\x8B\x08") 156 MAGIC_NUMBER("audio/x-pn-realaudio", "\x2E\x52\x4D\x46") 157 MAGIC_NUMBER("video/x-ms-asf", 158 "\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C") 159 MAGIC_NUMBER("image/tiff", "I I") 160 MAGIC_NUMBER("image/tiff", "II*") 161 MAGIC_NUMBER("image/tiff", "MM\x00*") 162 MAGIC_NUMBER("audio/mpeg", "ID3") 163 MAGIC_NUMBER("image/webp", "RIFF....WEBPVP8 ") 164 MAGIC_NUMBER("video/webm", "\x1A\x45\xDF\xA3") 165 // TODO(abarth): we don't handle partial byte matches yet 166 // MAGIC_NUMBER("video/mpeg", "\x00\x00\x01\xB") 167 // MAGIC_NUMBER("audio/mpeg", "\xFF\xE") 168 // MAGIC_NUMBER("audio/mpeg", "\xFF\xF") 169 MAGIC_NUMBER("application/zip", "PK\x03\x04") 170 MAGIC_NUMBER("application/x-rar-compressed", "Rar!\x1A\x07\x00") 171 MAGIC_NUMBER("application/x-msmetafile", "\xD7\xCD\xC6\x9A") 172 MAGIC_NUMBER("application/octet-stream", "MZ") // EXE 173 // Sniffing for Flash: 174 // 175 // MAGIC_NUMBER("application/x-shockwave-flash", "CWS") 176 // MAGIC_NUMBER("application/x-shockwave-flash", "FLV") 177 // MAGIC_NUMBER("application/x-shockwave-flash", "FWS") 178 // 179 // Including these magic number for Flash is a trade off. 180 // 181 // Pros: 182 // * Flash is an important and popular file format 183 // 184 // Cons: 185 // * These patterns are fairly weak 186 // * If we mistakenly decide something is Flash, we will execute it 187 // in the origin of an unsuspecting site. This could be a security 188 // vulnerability if the site allows users to upload content. 189 // 190 // On balance, we do not include these patterns. 191 }; 192 193 // The number of content bytes we need to use all our Microsoft Office magic 194 // numbers. 195 static const size_t kBytesRequiredForOfficeMagic = 8; 196 197 static const MagicNumber kOfficeMagicNumbers[] = { 198 MAGIC_NUMBER("CFB", "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1") 199 MAGIC_NUMBER("OOXML", "PK\x03\x04") 200 }; 201 202 enum OfficeDocType { 203 DOC_TYPE_WORD, 204 DOC_TYPE_EXCEL, 205 DOC_TYPE_POWERPOINT, 206 DOC_TYPE_NONE 207 }; 208 209 struct OfficeExtensionType { 210 OfficeDocType doc_type; 211 const char* extension; 212 size_t extension_len; 213 }; 214 215 #define OFFICE_EXTENSION(type, extension) \ 216 { (type), (extension), sizeof(extension) - 1 }, 217 218 static const OfficeExtensionType kOfficeExtensionTypes[] = { 219 OFFICE_EXTENSION(DOC_TYPE_WORD, ".doc") 220 OFFICE_EXTENSION(DOC_TYPE_EXCEL, ".xls") 221 OFFICE_EXTENSION(DOC_TYPE_POWERPOINT, ".ppt") 222 OFFICE_EXTENSION(DOC_TYPE_WORD, ".docx") 223 OFFICE_EXTENSION(DOC_TYPE_EXCEL, ".xlsx") 224 OFFICE_EXTENSION(DOC_TYPE_POWERPOINT, ".pptx") 225 }; 226 227 static const MagicNumber kExtraMagicNumbers[] = { 228 MAGIC_NUMBER("image/x-xbitmap", "#define") 229 MAGIC_NUMBER("image/x-icon", "\x00\x00\x01\x00") 230 MAGIC_NUMBER("image/svg+xml", "<?xml_version=") 231 MAGIC_NUMBER("audio/wav", "RIFF....WAVEfmt ") 232 MAGIC_NUMBER("video/avi", "RIFF....AVI LIST") 233 MAGIC_NUMBER("audio/ogg", "OggS") 234 MAGIC_MASK("video/mpeg", "\x00\x00\x01\xB0", "\xFF\xFF\xFF\xF0") 235 MAGIC_MASK("audio/mpeg", "\xFF\xE0", "\xFF\xE0") 236 MAGIC_NUMBER("video/3gpp", "....ftyp3g") 237 MAGIC_NUMBER("video/3gpp", "....ftypavcl") 238 MAGIC_NUMBER("video/mp4", "....ftyp") 239 MAGIC_NUMBER("video/quicktime", "....moov") 240 MAGIC_NUMBER("application/x-shockwave-flash", "CWS") 241 MAGIC_NUMBER("application/x-shockwave-flash", "FWS") 242 MAGIC_NUMBER("video/x-flv", "FLV") 243 MAGIC_NUMBER("audio/x-flac", "fLaC") 244 245 // RAW image types. 246 MAGIC_NUMBER("image/x-canon-cr2", "II\x2a\x00\x10\x00\x00\x00CR") 247 MAGIC_NUMBER("image/x-canon-crw", "II\x1a\x00\x00\x00HEAPCCDR") 248 MAGIC_NUMBER("image/x-minolta-mrw", "\x00MRM") 249 MAGIC_NUMBER("image/x-olympus-orf", "MMOR") // big-endian 250 MAGIC_NUMBER("image/x-olympus-orf", "IIRO") // little-endian 251 MAGIC_NUMBER("image/x-olympus-orf", "IIRS") // little-endian 252 MAGIC_NUMBER("image/x-fuji-raf", "FUJIFILMCCD-RAW ") 253 MAGIC_NUMBER("image/x-panasonic-raw", 254 "IIU\x00\x08\x00\x00\x00") // Panasonic .raw 255 MAGIC_NUMBER("image/x-panasonic-raw", 256 "IIU\x00\x18\x00\x00\x00") // Panasonic .rw2 257 MAGIC_NUMBER("image/x-phaseone-raw", "MMMMRaw") 258 MAGIC_NUMBER("image/x-x3f", "FOVb") 259 }; 260 261 // Our HTML sniffer differs slightly from Mozilla. For example, Mozilla will 262 // decide that a document that begins "<!DOCTYPE SOAP-ENV:Envelope PUBLIC " is 263 // HTML, but we will not. 264 265 #define MAGIC_HTML_TAG(tag) \ 266 MAGIC_STRING("text/html", "<" tag) 267 268 static const MagicNumber kSniffableTags[] = { 269 // XML processing directive. Although this is not an HTML mime type, we sniff 270 // for this in the HTML phase because text/xml is just as powerful as HTML and 271 // we want to leverage our white space skipping technology. 272 MAGIC_NUMBER("text/xml", "<?xml") // Mozilla 273 // DOCTYPEs 274 MAGIC_HTML_TAG("!DOCTYPE html") // HTML5 spec 275 // Sniffable tags, ordered by how often they occur in sniffable documents. 276 MAGIC_HTML_TAG("script") // HTML5 spec, Mozilla 277 MAGIC_HTML_TAG("html") // HTML5 spec, Mozilla 278 MAGIC_HTML_TAG("!--") 279 MAGIC_HTML_TAG("head") // HTML5 spec, Mozilla 280 MAGIC_HTML_TAG("iframe") // Mozilla 281 MAGIC_HTML_TAG("h1") // Mozilla 282 MAGIC_HTML_TAG("div") // Mozilla 283 MAGIC_HTML_TAG("font") // Mozilla 284 MAGIC_HTML_TAG("table") // Mozilla 285 MAGIC_HTML_TAG("a") // Mozilla 286 MAGIC_HTML_TAG("style") // Mozilla 287 MAGIC_HTML_TAG("title") // Mozilla 288 MAGIC_HTML_TAG("b") // Mozilla 289 MAGIC_HTML_TAG("body") // Mozilla 290 MAGIC_HTML_TAG("br") 291 MAGIC_HTML_TAG("p") // Mozilla 292 }; 293 294 static base::HistogramBase* UMASnifferHistogramGet(const char* name, 295 int array_size) { 296 base::HistogramBase* counter = 297 base::LinearHistogram::FactoryGet(name, 1, array_size - 1, array_size, 298 base::HistogramBase::kUmaTargetedHistogramFlag); 299 return counter; 300 } 301 302 // Compare content header to a magic number where magic_entry can contain '.' 303 // for single character of anything, allowing some bytes to be skipped. 304 static bool MagicCmp(const char* magic_entry, const char* content, size_t len) { 305 while (len) { 306 if ((*magic_entry != '.') && (*magic_entry != *content)) 307 return false; 308 ++magic_entry; 309 ++content; 310 --len; 311 } 312 return true; 313 } 314 315 // Like MagicCmp() except that it ANDs each byte with a mask before 316 // the comparison, because there are some bits we don't care about. 317 static bool MagicMaskCmp(const char* magic_entry, 318 const char* content, 319 size_t len, 320 const char* mask) { 321 while (len) { 322 if ((*magic_entry != '.') && (*magic_entry != (*mask & *content))) 323 return false; 324 ++magic_entry; 325 ++content; 326 ++mask; 327 --len; 328 } 329 return true; 330 } 331 332 static bool MatchMagicNumber(const char* content, 333 size_t size, 334 const MagicNumber& magic_entry, 335 std::string* result) { 336 const size_t len = magic_entry.magic_len; 337 338 // Keep kBytesRequiredForMagic honest. 339 DCHECK_LE(len, kBytesRequiredForMagic); 340 341 // To compare with magic strings, we need to compute strlen(content), but 342 // content might not actually have a null terminator. In that case, we 343 // pretend the length is content_size. 344 const char* end = static_cast<const char*>(memchr(content, '\0', size)); 345 const size_t content_strlen = 346 (end != NULL) ? static_cast<size_t>(end - content) : size; 347 348 bool match = false; 349 if (magic_entry.is_string) { 350 if (content_strlen >= len) { 351 // String comparisons are case-insensitive 352 match = (base::strncasecmp(magic_entry.magic, content, len) == 0); 353 } 354 } else { 355 if (size >= len) { 356 if (!magic_entry.mask) { 357 match = MagicCmp(magic_entry.magic, content, len); 358 } else { 359 match = MagicMaskCmp(magic_entry.magic, content, len, magic_entry.mask); 360 } 361 } 362 } 363 364 if (match) { 365 result->assign(magic_entry.mime_type); 366 return true; 367 } 368 return false; 369 } 370 371 static bool CheckForMagicNumbers(const char* content, size_t size, 372 const MagicNumber* magic, size_t magic_len, 373 base::HistogramBase* counter, 374 std::string* result) { 375 for (size_t i = 0; i < magic_len; ++i) { 376 if (MatchMagicNumber(content, size, magic[i], result)) { 377 if (counter) counter->Add(static_cast<int>(i)); 378 return true; 379 } 380 } 381 return false; 382 } 383 384 // Truncates |size| to |max_size| and returns true if |size| is at least 385 // |max_size|. 386 static bool TruncateSize(const size_t max_size, size_t* size) { 387 // Keep kMaxBytesToSniff honest. 388 DCHECK_LE(static_cast<int>(max_size), kMaxBytesToSniff); 389 390 if (*size >= max_size) { 391 *size = max_size; 392 return true; 393 } 394 return false; 395 } 396 397 // Returns true and sets result if the content appears to be HTML. 398 // Clears have_enough_content if more data could possibly change the result. 399 static bool SniffForHTML(const char* content, 400 size_t size, 401 bool* have_enough_content, 402 std::string* result) { 403 // For HTML, we are willing to consider up to 512 bytes. This may be overly 404 // conservative as IE only considers 256. 405 *have_enough_content &= TruncateSize(512, &size); 406 407 // We adopt a strategy similar to that used by Mozilla to sniff HTML tags, 408 // but with some modifications to better match the HTML5 spec. 409 const char* const end = content + size; 410 const char* pos; 411 for (pos = content; pos < end; ++pos) { 412 if (!IsAsciiWhitespace(*pos)) 413 break; 414 } 415 static base::HistogramBase* counter(NULL); 416 if (!counter) { 417 counter = UMASnifferHistogramGet("mime_sniffer.kSniffableTags2", 418 arraysize(kSniffableTags)); 419 } 420 // |pos| now points to first non-whitespace character (or at end). 421 return CheckForMagicNumbers(pos, end - pos, 422 kSniffableTags, arraysize(kSniffableTags), 423 counter, result); 424 } 425 426 // Returns true and sets result if the content matches any of kMagicNumbers. 427 // Clears have_enough_content if more data could possibly change the result. 428 static bool SniffForMagicNumbers(const char* content, 429 size_t size, 430 bool* have_enough_content, 431 std::string* result) { 432 *have_enough_content &= TruncateSize(kBytesRequiredForMagic, &size); 433 434 // Check our big table of Magic Numbers 435 static base::HistogramBase* counter(NULL); 436 if (!counter) { 437 counter = UMASnifferHistogramGet("mime_sniffer.kMagicNumbers2", 438 arraysize(kMagicNumbers)); 439 } 440 return CheckForMagicNumbers(content, size, 441 kMagicNumbers, arraysize(kMagicNumbers), 442 counter, result); 443 } 444 445 // Returns true and sets result if the content matches any of 446 // kOfficeMagicNumbers, and the URL has the proper extension. 447 // Clears |have_enough_content| if more data could possibly change the result. 448 static bool SniffForOfficeDocs(const char* content, 449 size_t size, 450 const GURL& url, 451 bool* have_enough_content, 452 std::string* result) { 453 *have_enough_content &= TruncateSize(kBytesRequiredForOfficeMagic, &size); 454 455 // Check our table of magic numbers for Office file types. 456 std::string office_version; 457 if (!CheckForMagicNumbers(content, size, 458 kOfficeMagicNumbers, arraysize(kOfficeMagicNumbers), 459 NULL, &office_version)) 460 return false; 461 462 OfficeDocType type = DOC_TYPE_NONE; 463 for (size_t i = 0; i < arraysize(kOfficeExtensionTypes); ++i) { 464 std::string url_path = url.path(); 465 466 if (url_path.length() < kOfficeExtensionTypes[i].extension_len) 467 continue; 468 469 const char* extension = 470 &url_path[url_path.length() - kOfficeExtensionTypes[i].extension_len]; 471 472 if (0 == base::strncasecmp(extension, kOfficeExtensionTypes[i].extension, 473 kOfficeExtensionTypes[i].extension_len)) { 474 type = kOfficeExtensionTypes[i].doc_type; 475 break; 476 } 477 } 478 479 if (type == DOC_TYPE_NONE) 480 return false; 481 482 if (office_version == "CFB") { 483 switch (type) { 484 case DOC_TYPE_WORD: 485 *result = "application/msword"; 486 return true; 487 case DOC_TYPE_EXCEL: 488 *result = "application/vnd.ms-excel"; 489 return true; 490 case DOC_TYPE_POWERPOINT: 491 *result = "application/vnd.ms-powerpoint"; 492 return true; 493 case DOC_TYPE_NONE: 494 NOTREACHED(); 495 return false; 496 } 497 } else if (office_version == "OOXML") { 498 switch (type) { 499 case DOC_TYPE_WORD: 500 *result = "application/vnd.openxmlformats-officedocument." 501 "wordprocessingml.document"; 502 return true; 503 case DOC_TYPE_EXCEL: 504 *result = "application/vnd.openxmlformats-officedocument." 505 "spreadsheetml.sheet"; 506 return true; 507 case DOC_TYPE_POWERPOINT: 508 *result = "application/vnd.openxmlformats-officedocument." 509 "presentationml.presentation"; 510 return true; 511 case DOC_TYPE_NONE: 512 NOTREACHED(); 513 return false; 514 } 515 } 516 517 NOTREACHED(); 518 return false; 519 } 520 521 static bool IsOfficeType(const std::string& type_hint) { 522 return (type_hint == "application/msword" || 523 type_hint == "application/vnd.ms-excel" || 524 type_hint == "application/vnd.ms-powerpoint" || 525 type_hint == "application/vnd.openxmlformats-officedocument." 526 "wordprocessingml.document" || 527 type_hint == "application/vnd.openxmlformats-officedocument." 528 "spreadsheetml.sheet" || 529 type_hint == "application/vnd.openxmlformats-officedocument." 530 "presentationml.presentation" || 531 type_hint == "application/vnd.ms-excel.sheet.macroenabled.12" || 532 type_hint == "application/vnd.ms-word.document.macroenabled.12" || 533 type_hint == "application/vnd.ms-powerpoint.presentation." 534 "macroenabled.12" || 535 type_hint == "application/mspowerpoint" || 536 type_hint == "application/msexcel" || 537 type_hint == "application/vnd.ms-word" || 538 type_hint == "application/vnd.ms-word.document.12" || 539 type_hint == "application/vnd.msword"); 540 } 541 542 // This function checks for files that have a Microsoft Office MIME type 543 // set, but are not actually Office files. 544 // 545 // If this is not actually an Office file, |*result| is set to 546 // "application/octet-stream", otherwise it is not modified. 547 // 548 // Returns false if additional data is required to determine the file type, or 549 // true if there is enough data to make a decision. 550 static bool SniffForInvalidOfficeDocs(const char* content, 551 size_t size, 552 const GURL& url, 553 std::string* result) { 554 if (!TruncateSize(kBytesRequiredForOfficeMagic, &size)) 555 return false; 556 557 // Check our table of magic numbers for Office file types. If it does not 558 // match one, the MIME type was invalid. Set it instead to a safe value. 559 std::string office_version; 560 if (!CheckForMagicNumbers(content, size, 561 kOfficeMagicNumbers, arraysize(kOfficeMagicNumbers), 562 NULL, &office_version)) { 563 *result = "application/octet-stream"; 564 } 565 566 // We have enough information to determine if this was a Microsoft Office 567 // document or not, so sniffing is completed. 568 return true; 569 } 570 571 // Byte order marks 572 static const MagicNumber kMagicXML[] = { 573 // We want to be very conservative in interpreting text/xml content as 574 // XHTML -- we just want to sniff enough to make unit tests pass. 575 // So we match explicitly on this, and don't match other ways of writing 576 // it in semantically-equivalent ways. 577 MAGIC_STRING("application/xhtml+xml", 578 "<html xmlns=\"http://www.w3.org/1999/xhtml\"") 579 MAGIC_STRING("application/atom+xml", "<feed") 580 MAGIC_STRING("application/rss+xml", "<rss") // UTF-8 581 }; 582 583 // Returns true and sets result if the content appears to contain XHTML or a 584 // feed. 585 // Clears have_enough_content if more data could possibly change the result. 586 // 587 // TODO(evanm): this is similar but more conservative than what Safari does, 588 // while HTML5 has a different recommendation -- what should we do? 589 // TODO(evanm): this is incorrect for documents whose encoding isn't a superset 590 // of ASCII -- do we care? 591 static bool SniffXML(const char* content, 592 size_t size, 593 bool* have_enough_content, 594 std::string* result) { 595 // We allow at most 300 bytes of content before we expect the opening tag. 596 *have_enough_content &= TruncateSize(300, &size); 597 const char* pos = content; 598 const char* const end = content + size; 599 600 // This loop iterates through tag-looking offsets in the file. 601 // We want to skip XML processing instructions (of the form "<?xml ...") 602 // and stop at the first "plain" tag, then make a decision on the mime-type 603 // based on the name (or possibly attributes) of that tag. 604 static base::HistogramBase* counter(NULL); 605 if (!counter) { 606 counter = UMASnifferHistogramGet("mime_sniffer.kMagicXML2", 607 arraysize(kMagicXML)); 608 } 609 const int kMaxTagIterations = 5; 610 for (int i = 0; i < kMaxTagIterations && pos < end; ++i) { 611 pos = reinterpret_cast<const char*>(memchr(pos, '<', end - pos)); 612 if (!pos) 613 return false; 614 615 if (base::strncasecmp(pos, "<?xml", sizeof("<?xml") - 1) == 0) { 616 // Skip XML declarations. 617 ++pos; 618 continue; 619 } else if (base::strncasecmp(pos, "<!DOCTYPE", 620 sizeof("<!DOCTYPE") - 1) == 0) { 621 // Skip DOCTYPE declarations. 622 ++pos; 623 continue; 624 } 625 626 if (CheckForMagicNumbers(pos, end - pos, 627 kMagicXML, arraysize(kMagicXML), 628 counter, result)) 629 return true; 630 631 // TODO(evanm): handle RSS 1.0, which is an RDF format and more difficult 632 // to identify. 633 634 // If we get here, we've hit an initial tag that hasn't matched one of the 635 // above tests. Abort. 636 return true; 637 } 638 639 // We iterated too far without finding a start tag. 640 // If we have more content to look at, we aren't going to change our mind by 641 // seeing more bytes from the network. 642 return pos < end; 643 } 644 645 // Byte order marks 646 static const MagicNumber kByteOrderMark[] = { 647 MAGIC_NUMBER("text/plain", "\xFE\xFF") // UTF-16BE 648 MAGIC_NUMBER("text/plain", "\xFF\xFE") // UTF-16LE 649 MAGIC_NUMBER("text/plain", "\xEF\xBB\xBF") // UTF-8 650 }; 651 652 // Whether a given byte looks like it might be part of binary content. 653 // Source: HTML5 spec 654 static char kByteLooksBinary[] = { 655 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, // 0x00 - 0x0F 656 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1F 657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 - 0x2F 658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30 - 0x3F 659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x4F 660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x5F 661 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x6F 662 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x7F 663 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 - 0x8F 664 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9F 665 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0 - 0xAF 666 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0 - 0xBF 667 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0 - 0xCF 668 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0 - 0xDF 669 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0 - 0xEF 670 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xF0 - 0xFF 671 }; 672 673 // Returns true and sets result to "application/octet-stream" if the content 674 // appears to be binary data. Otherwise, returns false and sets "text/plain". 675 // Clears have_enough_content if more data could possibly change the result. 676 static bool SniffBinary(const char* content, 677 size_t size, 678 bool* have_enough_content, 679 std::string* result) { 680 // There is no concensus about exactly how to sniff for binary content. 681 // * IE 7: Don't sniff for binary looking bytes, but trust the file extension. 682 // * Firefox 3.5: Sniff first 4096 bytes for a binary looking byte. 683 // Here, we side with FF, but with a smaller buffer. This size was chosen 684 // because it is small enough to comfortably fit into a single packet (after 685 // allowing for headers) and yet large enough to account for binary formats 686 // that have a significant amount of ASCII at the beginning (crbug.com/15314). 687 const bool is_truncated = TruncateSize(kMaxBytesToSniff, &size); 688 689 // First, we look for a BOM. 690 static base::HistogramBase* counter(NULL); 691 if (!counter) { 692 counter = UMASnifferHistogramGet("mime_sniffer.kByteOrderMark2", 693 arraysize(kByteOrderMark)); 694 } 695 std::string unused; 696 if (CheckForMagicNumbers(content, size, 697 kByteOrderMark, arraysize(kByteOrderMark), 698 counter, &unused)) { 699 // If there is BOM, we think the buffer is not binary. 700 result->assign("text/plain"); 701 return false; 702 } 703 704 // Next we look to see if any of the bytes "look binary." 705 for (size_t i = 0; i < size; ++i) { 706 // If we a see a binary-looking byte, we think the content is binary. 707 if (kByteLooksBinary[static_cast<unsigned char>(content[i])]) { 708 result->assign("application/octet-stream"); 709 return true; 710 } 711 } 712 713 // No evidence either way. Default to non-binary and, if truncated, clear 714 // have_enough_content because there could be a binary looking byte in the 715 // truncated data. 716 *have_enough_content &= is_truncated; 717 result->assign("text/plain"); 718 return false; 719 } 720 721 static bool IsUnknownMimeType(const std::string& mime_type) { 722 // TODO(tc): Maybe reuse some code in net/http/http_response_headers.* here. 723 // If we do, please be careful not to alter the semantics at all. 724 static const char* kUnknownMimeTypes[] = { 725 // Empty mime types are as unknown as they get. 726 "", 727 // The unknown/unknown type is popular and uninformative 728 "unknown/unknown", 729 // The second most popular unknown mime type is application/unknown 730 "application/unknown", 731 // Firefox rejects a mime type if it is exactly */* 732 "*/*", 733 }; 734 static base::HistogramBase* counter(NULL); 735 if (!counter) { 736 counter = UMASnifferHistogramGet("mime_sniffer.kUnknownMimeTypes2", 737 arraysize(kUnknownMimeTypes) + 1); 738 } 739 for (size_t i = 0; i < arraysize(kUnknownMimeTypes); ++i) { 740 if (mime_type == kUnknownMimeTypes[i]) { 741 counter->Add(i); 742 return true; 743 } 744 } 745 if (mime_type.find('/') == std::string::npos) { 746 // Firefox rejects a mime type if it does not contain a slash 747 counter->Add(arraysize(kUnknownMimeTypes)); 748 return true; 749 } 750 return false; 751 } 752 753 // Returns true and sets result if the content appears to be a crx (Chrome 754 // extension) file. 755 // Clears have_enough_content if more data could possibly change the result. 756 static bool SniffCRX(const char* content, 757 size_t size, 758 const GURL& url, 759 const std::string& type_hint, 760 bool* have_enough_content, 761 std::string* result) { 762 static base::HistogramBase* counter(NULL); 763 if (!counter) 764 counter = UMASnifferHistogramGet("mime_sniffer.kSniffCRX", 3); 765 766 // Technically, the crx magic number is just Cr24, but the bytes after that 767 // are a version number which changes infrequently. Including it in the 768 // sniffing gives us less room for error. If the version number ever changes, 769 // we can just add an entry to this list. 770 // 771 // TODO(aa): If we ever have another magic number, we'll want to pass a 772 // histogram into CheckForMagicNumbers(), below, to see which one matched. 773 static const struct MagicNumber kCRXMagicNumbers[] = { 774 MAGIC_NUMBER("application/x-chrome-extension", "Cr24\x02\x00\x00\x00") 775 }; 776 777 // Only consider files that have the extension ".crx". 778 static const char kCRXExtension[] = ".crx"; 779 // Ignore null by subtracting 1. 780 static const int kExtensionLength = arraysize(kCRXExtension) - 1; 781 if (url.path().rfind(kCRXExtension, std::string::npos, kExtensionLength) == 782 url.path().size() - kExtensionLength) { 783 counter->Add(1); 784 } else { 785 return false; 786 } 787 788 *have_enough_content &= TruncateSize(kBytesRequiredForMagic, &size); 789 if (CheckForMagicNumbers(content, size, 790 kCRXMagicNumbers, arraysize(kCRXMagicNumbers), 791 NULL, result)) { 792 counter->Add(2); 793 } else { 794 return false; 795 } 796 797 return true; 798 } 799 800 bool ShouldSniffMimeType(const GURL& url, const std::string& mime_type) { 801 static base::HistogramBase* should_sniff_counter(NULL); 802 if (!should_sniff_counter) { 803 should_sniff_counter = 804 UMASnifferHistogramGet("mime_sniffer.ShouldSniffMimeType2", 3); 805 } 806 bool sniffable_scheme = url.is_empty() || 807 url.SchemeIsHTTPOrHTTPS() || 808 url.SchemeIs("ftp") || 809 #if defined(OS_ANDROID) 810 url.SchemeIs("content") || 811 #endif 812 url.SchemeIsFile() || 813 url.SchemeIsFileSystem(); 814 if (!sniffable_scheme) { 815 should_sniff_counter->Add(1); 816 return false; 817 } 818 819 static const char* kSniffableTypes[] = { 820 // Many web servers are misconfigured to send text/plain for many 821 // different types of content. 822 "text/plain", 823 // We want to sniff application/octet-stream for 824 // application/x-chrome-extension, but nothing else. 825 "application/octet-stream", 826 // XHTML and Atom/RSS feeds are often served as plain xml instead of 827 // their more specific mime types. 828 "text/xml", 829 "application/xml", 830 // Check for false Microsoft Office MIME types. 831 "application/msword", 832 "application/vnd.ms-excel", 833 "application/vnd.ms-powerpoint", 834 "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 835 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 836 "application/vnd.openxmlformats-officedocument.presentationml.presentation", 837 "application/vnd.ms-excel.sheet.macroenabled.12", 838 "application/vnd.ms-word.document.macroenabled.12", 839 "application/vnd.ms-powerpoint.presentation.macroenabled.12", 840 "application/mspowerpoint", 841 "application/msexcel", 842 "application/vnd.ms-word", 843 "application/vnd.ms-word.document.12", 844 "application/vnd.msword", 845 }; 846 static base::HistogramBase* counter(NULL); 847 if (!counter) { 848 counter = UMASnifferHistogramGet("mime_sniffer.kSniffableTypes2", 849 arraysize(kSniffableTypes) + 1); 850 } 851 for (size_t i = 0; i < arraysize(kSniffableTypes); ++i) { 852 if (mime_type == kSniffableTypes[i]) { 853 counter->Add(i); 854 should_sniff_counter->Add(2); 855 return true; 856 } 857 } 858 if (IsUnknownMimeType(mime_type)) { 859 // The web server didn't specify a content type or specified a mime 860 // type that we ignore. 861 counter->Add(arraysize(kSniffableTypes)); 862 should_sniff_counter->Add(2); 863 return true; 864 } 865 should_sniff_counter->Add(1); 866 return false; 867 } 868 869 bool SniffMimeType(const char* content, 870 size_t content_size, 871 const GURL& url, 872 const std::string& type_hint, 873 std::string* result) { 874 DCHECK_LT(content_size, 1000000U); // sanity check 875 DCHECK(content); 876 DCHECK(result); 877 878 // By default, we assume we have enough content. 879 // Each sniff routine may unset this if it wasn't provided enough content. 880 bool have_enough_content = true; 881 882 // By default, we'll return the type hint. 883 // Each sniff routine may modify this if it has a better guess.. 884 result->assign(type_hint); 885 886 // If the file has a Microsoft Office MIME type, we should only check that it 887 // is a valid Office file. Because this is the only reason we sniff files 888 // with a Microsoft Office MIME type, we can return early. 889 if (IsOfficeType(type_hint)) 890 return SniffForInvalidOfficeDocs(content, content_size, url, result); 891 892 // Cache information about the type_hint 893 const bool hint_is_unknown_mime_type = IsUnknownMimeType(type_hint); 894 895 // First check for HTML 896 if (hint_is_unknown_mime_type) { 897 // We're only willing to sniff HTML if the server has not supplied a mime 898 // type, or if the type it did supply indicates that it doesn't know what 899 // the type should be. 900 if (SniffForHTML(content, content_size, &have_enough_content, result)) 901 return true; // We succeeded in sniffing HTML. No more content needed. 902 } 903 904 // We're only willing to sniff for binary in 3 cases: 905 // 1. The server has not supplied a mime type. 906 // 2. The type it did supply indicates that it doesn't know what the type 907 // should be. 908 // 3. The type is "text/plain" which is the default on some web servers and 909 // could be indicative of a mis-configuration that we shield the user from. 910 const bool hint_is_text_plain = (type_hint == "text/plain"); 911 if (hint_is_unknown_mime_type || hint_is_text_plain) { 912 if (!SniffBinary(content, content_size, &have_enough_content, result)) { 913 // If the server said the content was text/plain and it doesn't appear 914 // to be binary, then we trust it. 915 if (hint_is_text_plain) { 916 return have_enough_content; 917 } 918 } 919 } 920 921 // If we have plain XML, sniff XML subtypes. 922 if (type_hint == "text/xml" || type_hint == "application/xml") { 923 // We're not interested in sniffing these types for images and the like. 924 // Instead, we're looking explicitly for a feed. If we don't find one 925 // we're done and return early. 926 if (SniffXML(content, content_size, &have_enough_content, result)) 927 return true; 928 return have_enough_content; 929 } 930 931 // CRX files (Chrome extensions) have a special sniffing algorithm. It is 932 // tighter than the others because we don't have to match legacy behavior. 933 if (SniffCRX(content, content_size, url, type_hint, 934 &have_enough_content, result)) 935 return true; 936 937 // Check the file extension and magic numbers to see if this is an Office 938 // document. This needs to be checked before the general magic numbers 939 // because zip files and Office documents (OOXML) have the same magic number. 940 if (SniffForOfficeDocs(content, content_size, url, 941 &have_enough_content, result)) 942 return true; // We've matched a magic number. No more content needed. 943 944 // We're not interested in sniffing for magic numbers when the type_hint 945 // is application/octet-stream. Time to bail out. 946 if (type_hint == "application/octet-stream") 947 return have_enough_content; 948 949 // Now we look in our large table of magic numbers to see if we can find 950 // anything that matches the content. 951 if (SniffForMagicNumbers(content, content_size, 952 &have_enough_content, result)) 953 return true; // We've matched a magic number. No more content needed. 954 955 return have_enough_content; 956 } 957 958 bool SniffMimeTypeFromLocalData(const char* content, 959 size_t size, 960 std::string* result) { 961 // First check the extra table. 962 if (CheckForMagicNumbers(content, size, kExtraMagicNumbers, 963 arraysize(kExtraMagicNumbers), NULL, result)) 964 return true; 965 // Finally check the original table. 966 return CheckForMagicNumbers(content, size, kMagicNumbers, 967 arraysize(kMagicNumbers), NULL, result); 968 } 969 970 } // namespace net 971