diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-05 14:08:31 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-11 07:46:53 +0000 |
commit | 6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (patch) | |
tree | ab00f70a5e89278d6a0d16ff0c42578dc4d84a2d /chromium/net/ftp | |
parent | e733310db58160074f574c429d48f8308c0afe17 (diff) | |
download | qtwebengine-chromium-6a4cabb866f66d4128a97cdc6d9d08ce074f1247.tar.gz |
BASELINE: Update Chromium to 57.0.2987.144
Change-Id: I29db402ff696c71a04c4dbaec822c2e53efe0267
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/net/ftp')
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser.cc | 4 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser_ls.cc | 5 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser_unittest.cc | 8 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser_unittest.h | 9 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser_vms.cc | 41 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc | 129 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_network_session.cc | 1 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_network_transaction.cc | 109 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_network_transaction_unittest.cc | 268 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_util.cc | 16 | ||||
-rw-r--r-- | chromium/net/ftp/ftp_util_unittest.cc | 53 |
11 files changed, 403 insertions, 240 deletions
diff --git a/chromium/net/ftp/ftp_directory_listing_parser.cc b/chromium/net/ftp/ftp_directory_listing_parser.cc index 151c1b9df56..1096ae01f62 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser.cc +++ b/chromium/net/ftp/ftp_directory_listing_parser.cc @@ -28,7 +28,7 @@ int FillInRawName(const std::string& encoding, std::vector<FtpDirectoryListingEntry>* entries) { for (size_t i = 0; i < entries->size(); i++) { if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(), - base::OnStringConversionError::FAIL, + base::OnStringConversionError::SUBSTITUTE, &entries->at(i).raw_name)) { return ERR_ENCODING_CONVERSION_FAILED; } @@ -91,7 +91,7 @@ int DecodeAndParse(const std::string& text, base::string16 converted_text; if (base::CodepageToUTF16(text, encoding_name, - base::OnStringConversionError::FAIL, + base::OnStringConversionError::SUBSTITUTE, &converted_text)) { const char* const kNewlineSeparators[] = {"\n", "\r\n"}; diff --git a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc index 9d1b7116758..9ce1973756d 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc +++ b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc @@ -52,9 +52,8 @@ bool TwoColumnDateListingToTime(const base::string16& date, if (!time_exploded.HasValidValues()) return false; - // We don't know the time zone of the server, so just use local time. - *result = base::Time::FromLocalExploded(time_exploded); - return true; + // We don't know the time zone of the server, so just use UTC. + return base::Time::FromUTCExploded(time_exploded, result); } // Returns the column index of the end of the date listing and detected diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc index 583426d98d7..a7f5bcfb3da 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc +++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc @@ -48,8 +48,9 @@ TEST_P(FtpDirectoryListingParserTest, Parse) { mock_current_time_exploded.day_of_month = 15; mock_current_time_exploded.hour = 12; mock_current_time_exploded.minute = 45; - base::Time mock_current_time( - base::Time::FromLocalExploded(mock_current_time_exploded)); + base::Time mock_current_time; + EXPECT_TRUE(base::Time::FromUTCExploded(mock_current_time_exploded, + &mock_current_time)); SCOPED_TRACE(base::StringPrintf("Test case: %s", param.name)); @@ -109,7 +110,7 @@ TEST_P(FtpDirectoryListingParserTest, Parse) { EXPECT_EQ(size, entry.size); base::Time::Exploded time_exploded; - entry.last_modified.LocalExplode(&time_exploded); + entry.last_modified.UTCExplode(&time_exploded); EXPECT_EQ(year, time_exploded.year); EXPECT_EQ(month, time_exploded.month); EXPECT_EQ(day_of_month, time_exploded.day_of_month); @@ -157,6 +158,7 @@ const FtpTestParam kTestParams[] = { {"dir-listing-ls-31", OK}, {"dir-listing-ls-32", OK}, // busybox {"dir-listing-ls-33", OK}, + {"dir-listing-ls-34", OK}, // Broken encoding. Should not fail. {"dir-listing-netware-1", OK}, {"dir-listing-netware-2", OK}, diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h index 3cbe17ad2d6..f010299ff5e 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h +++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h @@ -49,7 +49,7 @@ class FtpDirectoryListingParserTest : public testing::Test { EXPECT_EQ(test_case.size, entry.size); base::Time::Exploded time_exploded; - entry.last_modified.LocalExplode(&time_exploded); + entry.last_modified.UTCExplode(&time_exploded); // Only test members displayed on the directory listing. EXPECT_EQ(test_case.year, time_exploded.year); @@ -68,11 +68,14 @@ class FtpDirectoryListingParserTest : public testing::Test { mock_current_time_exploded.day_of_month = 15; mock_current_time_exploded.hour = 12; mock_current_time_exploded.minute = 45; - return base::Time::FromLocalExploded(mock_current_time_exploded); + + base::Time out_time; + EXPECT_TRUE( + base::Time::FromUTCExploded(mock_current_time_exploded, &out_time)); + return out_time; } }; } // namespace net #endif // NET_FTP_FTP_DIRECTORY_LISTING_PARSER_UNITTEST_H_ - diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc index 8139ee0bc24..8f1bfe1dd83 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc +++ b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc @@ -6,6 +6,7 @@ #include <vector> +#include "base/numerics/safe_math.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -55,6 +56,24 @@ bool ParseVmsFilename(const base::string16& raw_filename, return true; } +// VMS's directory listing gives file size in blocks. The exact file size is +// unknown both because it is measured in blocks, but also because the block +// size is unknown (but assumed to be 512 bytes). +bool ApproximateFilesizeFromBlockCount(int64_t num_blocks, int64_t* out_size) { + if (num_blocks < 0) + return false; + + const int kBlockSize = 512; + base::CheckedNumeric<int64_t> num_bytes = num_blocks; + num_bytes *= kBlockSize; + + if (!num_bytes.IsValid()) + return false; // Block count is too large. + + *out_size = num_bytes.ValueOrDie(); + return true; +} + bool ParseVmsFilesize(const base::string16& input, int64_t* size) { if (base::ContainsOnlyChars(input, base::ASCIIToUTF16("*"))) { // Response consisting of asterisks means unknown size. @@ -62,17 +81,9 @@ bool ParseVmsFilesize(const base::string16& input, int64_t* size) { return true; } - // VMS's directory listing gives us file size in blocks. We assume that - // the block size is 512 bytes. It doesn't give accurate file size, but is the - // best information we have. - const int kBlockSize = 512; - - if (base::StringToInt64(input, size)) { - if (*size < 0) - return false; - *size *= kBlockSize; - return true; - } + int64_t num_blocks; + if (base::StringToInt64(input, &num_blocks)) + return ApproximateFilesizeFromBlockCount(num_blocks, size); std::vector<base::StringPiece16> parts = base::SplitStringPiece(input, base::ASCIIToUTF16("/"), @@ -90,8 +101,7 @@ bool ParseVmsFilesize(const base::string16& input, int64_t* size) { if (blocks_used < 0 || blocks_allocated < 0) return false; - *size = blocks_used * kBlockSize; - return true; + return ApproximateFilesizeFromBlockCount(blocks_used, size); } bool LooksLikeVmsFileProtectionListingPart(const base::string16& input) { @@ -192,9 +202,8 @@ bool VmsDateListingToTime(const std::vector<base::string16>& columns, if (!base::StringToInt(time_parts[1], &time_exploded.minute)) return false; - // We don't know the time zone of the server, so just use local time. - *time = base::Time::FromLocalExploded(time_exploded); - return true; + // We don't know the time zone of the server, so just use UTC. + return base::Time::FromUTCExploded(time_exploded, time); } } // namespace diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc index f566481b548..24d158876f1 100644 --- a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc +++ b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc @@ -21,36 +21,33 @@ typedef FtpDirectoryListingParserTest FtpDirectoryListingParserVmsTest; TEST_F(FtpDirectoryListingParserVmsTest, Good) { const struct SingleLineTestData good_cases[] = { - { "README.TXT;4 2 18-APR-2000 10:40:39.90", - FtpDirectoryListingEntry::FILE, "readme.txt", 1024, - 2000, 4, 18, 10, 40 }, - { ".WELCOME;1 2 13-FEB-2002 23:32:40.47", - FtpDirectoryListingEntry::FILE, ".welcome", 1024, - 2002, 2, 13, 23, 32 }, - { "FILE.;1 2 13-FEB-2002 23:32:40.47", - FtpDirectoryListingEntry::FILE, "file.", 1024, - 2002, 2, 13, 23, 32 }, - { "EXAMPLE.TXT;1 1 4-NOV-2009 06:02 [JOHNDOE] (RWED,RWED,,)", - FtpDirectoryListingEntry::FILE, "example.txt", 512, - 2009, 11, 4, 6, 2 }, - { "ANNOUNCE.TXT;2 1/16 12-MAR-2005 08:44:57 [SYSTEM] (RWED,RWED,RE,RE)", - FtpDirectoryListingEntry::FILE, "announce.txt", 512, - 2005, 3, 12, 8, 44 }, - { "TEST.DIR;1 1 4-MAR-1999 22:14:34 [UCX$NOBO,ANONYMOUS] (RWE,RWE,RWE,RWE)", - FtpDirectoryListingEntry::DIRECTORY, "test", -1, - 1999, 3, 4, 22, 14 }, - { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (,,,)", - FtpDirectoryListingEntry::FILE, "announce.txt", 512, - 2005, 3, 12, 8, 44 }, - { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (R,RW,RWD,RE)", - FtpDirectoryListingEntry::FILE, "announce.txt", 512, - 2005, 3, 12, 8, 44 }, - { "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (ED,RED,WD,WED)", - FtpDirectoryListingEntry::FILE, "announce.txt", 512, - 2005, 3, 12, 8, 44 }, - { "VMS721.ISO;2 ****** 6-MAY-2008 09:29 [ANONY,ANONYMOUS] (RE,RWED,RE,RE)", - FtpDirectoryListingEntry::FILE, "vms721.iso", -1, - 2008, 5, 6, 9, 29 }, + {"README.TXT;4 2 18-APR-2000 10:40:39.90", + FtpDirectoryListingEntry::FILE, "readme.txt", 1024, 2000, 4, 18, 10, 40}, + {".WELCOME;1 2 13-FEB-2002 23:32:40.47", + FtpDirectoryListingEntry::FILE, ".welcome", 1024, 2002, 2, 13, 23, 32}, + {"FILE.;1 2 13-FEB-2002 23:32:40.47", FtpDirectoryListingEntry::FILE, + "file.", 1024, 2002, 2, 13, 23, 32}, + {"EXAMPLE.TXT;1 1 4-NOV-2009 06:02 [JOHNDOE] (RWED,RWED,,)", + FtpDirectoryListingEntry::FILE, "example.txt", 512, 2009, 11, 4, 6, 2}, + {"ANNOUNCE.TXT;2 1/16 12-MAR-2005 08:44:57 [SYSTEM] (RWED,RWED,RE,RE)", + FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44}, + {"TEST.DIR;1 1 4-MAR-1999 22:14:34 [UCX$NOBO,ANONYMOUS] " + "(RWE,RWE,RWE,RWE)", + FtpDirectoryListingEntry::DIRECTORY, "test", -1, 1999, 3, 4, 22, 14}, + {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (,,,)", + FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44}, + {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (R,RW,RWD,RE)", + FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44}, + {"ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (ED,RED,WD,WED)", + FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44}, + {"VMS721.ISO;2 ****** 6-MAY-2008 09:29 [ANONY,ANONYMOUS] " + "(RE,RWED,RE,RE)", + FtpDirectoryListingEntry::FILE, "vms721.iso", -1, 2008, 5, 6, 9, 29}, + // This has an unusually large allocated block size (INT64_MAX), but + // shouldn't matter as it is not used. + {"ANNOUNCE.TXT;2 1/9223372036854775807 12-MAR-2005 08:44:57 [SYSTEM] " + "(RWED,RWED,RE,RE)", + FtpDirectoryListingEntry::FILE, "announce.txt", 512, 2005, 3, 12, 8, 44}, }; for (size_t i = 0; i < arraysize(good_cases); i++) { SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, @@ -76,37 +73,47 @@ TEST_F(FtpDirectoryListingParserVmsTest, Good) { TEST_F(FtpDirectoryListingParserVmsTest, Bad) { const char* const bad_cases[] = { - "garbage", - - // Missing file version number. - "README.TXT 2 18-APR-2000 10:40:39", - - // Missing extension. - "README;1 2 18-APR-2000 10:40:39", - - // Malformed file size. - "README.TXT;1 garbage 18-APR-2000 10:40:39", - "README.TXT;1 -2 18-APR-2000 10:40:39", - - // Malformed date. - "README.TXT;1 2 APR-2000 10:40:39", - "README.TXT;1 2 -18-APR-2000 10:40:39", - "README.TXT;1 2 18-APR 10:40:39", - "README.TXT;1 2 18-APR-2000 10", - "README.TXT;1 2 18-APR-2000 10:40.25", - "README.TXT;1 2 18-APR-2000 10:40.25.25", - - // Malformed security information. - "X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]", - "X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)", - "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)", + "garbage", + + // Missing file version number. + "README.TXT 2 18-APR-2000 10:40:39", + + // Missing extension. + "README;1 2 18-APR-2000 10:40:39", + + // Malformed file size. + "README.TXT;1 garbage 18-APR-2000 10:40:39", + "README.TXT;1 -2 18-APR-2000 10:40:39", + + // Malformed date. + "README.TXT;1 2 APR-2000 10:40:39", + "README.TXT;1 2 -18-APR-2000 10:40:39", "README.TXT;1 2 18-APR 10:40:39", + "README.TXT;1 2 18-APR-2000 10", "README.TXT;1 2 18-APR-2000 10:40.25", + "README.TXT;1 2 18-APR-2000 10:40.25.25", + + // Malformed security information. + "X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]", + "X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)", + "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)", + + // Block size (INT64_MAX) is too large -- will overflow when + // multiplying by 512 to calculate the file size in bytes. + "README.TXT;1 9223372036854775807 18-APR-2000 10:40:39.90", + "README.TXT;1 9223372036854775807/9223372036854775807 18-APR-2000 " + "10:40:39.90", + + // Block size (larger than INT64_MAX) is too large -- will fail to + // parse to an int64_t + "README.TXT;1 19223372036854775807 18-APR-2000 10:40:39.90", + "README.TXT;1 19223372036854775807/19223372036854775807 18-APR-2000 " + "10:40:39.90", }; for (size_t i = 0; i < arraysize(bad_cases); i++) { SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, bad_cases[i])); diff --git a/chromium/net/ftp/ftp_network_session.cc b/chromium/net/ftp/ftp_network_session.cc index 65dd218a4d4..37a7cbee74d 100644 --- a/chromium/net/ftp/ftp_network_session.cc +++ b/chromium/net/ftp/ftp_network_session.cc @@ -12,4 +12,3 @@ FtpNetworkSession::FtpNetworkSession(HostResolver* host_resolver) FtpNetworkSession::~FtpNetworkSession() {} } // namespace net - diff --git a/chromium/net/ftp/ftp_network_transaction.cc b/chromium/net/ftp/ftp_network_transaction.cc index 8ecde3104de..dbe1154f826 100644 --- a/chromium/net/ftp/ftp_network_transaction.cc +++ b/chromium/net/ftp/ftp_network_transaction.cc @@ -16,6 +16,7 @@ #include "net/base/address_list.h" #include "net/base/escape.h" #include "net/base/net_errors.h" +#include "net/base/parse_number.h" #include "net/base/port_util.h" #include "net/base/url_util.h" #include "net/ftp/ftp_request_info.h" @@ -35,8 +36,8 @@ const char kCRLF[] = "\r\n"; const int kCtrlBufLen = 1024; -// Returns true if |input| can be safely used as a part of FTP command. -bool IsValidFTPCommandString(const std::string& input) { +// Returns true if |input| can be safely used as a part of an FTP command. +bool IsValidFTPCommandSubstring(const std::string& input) { // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII // characters in the command if the request path contains them. To be // compatible, we do the same and allow non-ASCII characters in a command. @@ -123,25 +124,32 @@ int GetNetErrorCodeForFtpResponseCode(int response_code) { bool ExtractPortFromEPSVResponse(const FtpCtrlResponse& response, int* port) { if (response.lines.size() != 1) return false; - const char* ptr = response.lines[0].c_str(); - while (*ptr && *ptr != '(') - ++ptr; - if (!*ptr) - return false; - char sep = *(++ptr); - if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep) + + base::StringPiece epsv_line(response.lines[0]); + size_t start = epsv_line.find('('); + // If the line doesn't have a '(' or doesn't have enough characters after the + // first '(', fail. + if (start == base::StringPiece::npos || epsv_line.length() - start < 7) return false; - if (!isdigit(*(++ptr))) + + char separator = epsv_line[start + 1]; + + // Make sure we have "(<d><d><d>...", where <d> is not a number. + if ((separator >= '0' && separator <= '9') || + epsv_line[start + 2] != separator || epsv_line[start + 3] != separator) { return false; - *port = *ptr - '0'; - while (isdigit(*(++ptr))) { - *port *= 10; - *port += *ptr - '0'; } - if (*ptr != sep) + + // Skip over those characters. + start += 4; + + // Make sure there's a terminal <d>. + size_t end = epsv_line.find(separator, start); + if (end == base::StringPiece::npos) return false; - return true; + return ParseInt32(epsv_line.substr(start, end - start), + ParseIntFormat::NON_NEGATIVE, port); } // There are two way we can receive IP address and port. @@ -190,13 +198,15 @@ bool ExtractPortFromPASVResponse(const FtpCtrlResponse& response, int* port) { // Ignore the IP address supplied in the response. We are always going // to connect back to the same server to prevent FTP PASV port scanning. - int p0, p1; - if (!base::StringToInt(pieces[4], &p0)) + uint32_t p0, p1; + if (!ParseUint32(pieces[4], &p0)) return false; - if (!base::StringToInt(pieces[5], &p1)) + if (!ParseUint32(pieces[5], &p1)) + return false; + if (p0 > 0xFF || p1 > 0xFF) return false; - *port = (p0 << 8) + p1; + *port = (p0 << 8) + p1; return true; } @@ -417,7 +427,9 @@ int FtpNetworkTransaction::ProcessCtrlResponse() { } // We may get multiple responses for some commands, - // see http://crbug.com/18036. + // see http://crbug.com/18036. Consume all responses, regardless of whether + // they make sense. On unexpected responses, SendFtpCommand expects all data + // to have already been consumed, even when sending the QUIT command. while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) { response = ctrl_response_buffer_->PopResponse(); @@ -430,7 +442,8 @@ int FtpNetworkTransaction::ProcessCtrlResponse() { break; default: // Multiple responses for other commands are invalid. - return Stop(ERR_INVALID_RESPONSE); + rv = Stop(ERR_INVALID_RESPONSE); + break; } } @@ -449,7 +462,7 @@ int FtpNetworkTransaction::SendFtpCommand(const std::string& command, DCHECK(!write_command_buf_.get()); DCHECK(!write_buf_.get()); - if (!IsValidFTPCommandString(command)) { + if (!IsValidFTPCommandSubstring(command)) { // Callers should validate the command themselves and return a more specific // error code. NOTREACHED(); @@ -492,7 +505,7 @@ std::string FtpNetworkTransaction::GetRequestPathForFtpCommand( UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS; // This may unescape to non-ASCII characters, but we allow that. See the - // comment for IsValidFTPCommandString. + // comment for IsValidFTPCommandSubstring. path = UnescapeURLComponent(path, unescape_rules); if (system_type_ == SYSTEM_TYPE_VMS) { @@ -502,7 +515,7 @@ std::string FtpNetworkTransaction::GetRequestPathForFtpCommand( path = FtpUtil::UnixFilePathToVMS(path); } - DCHECK(IsValidFTPCommandString(path)); + DCHECK(IsValidFTPCommandSubstring(path)); return path; } @@ -743,7 +756,7 @@ int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { int FtpNetworkTransaction::DoCtrlWriteUSER() { std::string command = "USER " + base::UTF16ToUTF8(credentials_.username()); - if (!IsValidFTPCommandString(command)) + if (!IsValidFTPCommandSubstring(command)) return Stop(ERR_MALFORMED_IDENTITY); next_state_ = STATE_CTRL_READ; @@ -753,6 +766,8 @@ int FtpNetworkTransaction::DoCtrlWriteUSER() { int FtpNetworkTransaction::ProcessResponseUSER( const FtpCtrlResponse& response) { switch (GetErrorClass(response.status_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_INVALID_RESPONSE); case ERROR_CLASS_OK: next_state_ = STATE_CTRL_WRITE_SYST; break; @@ -763,9 +778,6 @@ int FtpNetworkTransaction::ProcessResponseUSER( case ERROR_CLASS_PERMANENT_ERROR: response_.needs_auth = true; return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -774,7 +786,7 @@ int FtpNetworkTransaction::ProcessResponseUSER( int FtpNetworkTransaction::DoCtrlWritePASS() { std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password()); - if (!IsValidFTPCommandString(command)) + if (!IsValidFTPCommandSubstring(command)) return Stop(ERR_MALFORMED_IDENTITY); next_state_ = STATE_CTRL_READ; @@ -784,6 +796,8 @@ int FtpNetworkTransaction::DoCtrlWritePASS() { int FtpNetworkTransaction::ProcessResponsePASS( const FtpCtrlResponse& response) { switch (GetErrorClass(response.status_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_INVALID_RESPONSE); case ERROR_CLASS_OK: next_state_ = STATE_CTRL_WRITE_SYST; break; @@ -793,9 +807,6 @@ int FtpNetworkTransaction::ProcessResponsePASS( case ERROR_CLASS_PERMANENT_ERROR: response_.needs_auth = true; return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -853,9 +864,6 @@ int FtpNetworkTransaction::ProcessResponseSYST( // Server does not recognize the SYST command so proceed. next_state_ = STATE_CTRL_WRITE_PWD; break; - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -888,6 +896,11 @@ int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { line = FtpUtil::VMSPathToUnix(line); if (!line.empty() && line.back() == '/') line.erase(line.length() - 1); + // Fail if the "path" contains characters not allowed in commands. + // This does mean that files with CRs or LFs in their names aren't + // handled, but that's probably for the best. + if (!IsValidFTPCommandSubstring(line)) + return Stop(ERR_INVALID_RESPONSE); current_remote_directory_ = line; next_state_ = STATE_CTRL_WRITE_TYPE; break; @@ -898,9 +911,6 @@ int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) { return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); case ERROR_CLASS_PERMANENT_ERROR: return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -934,9 +944,6 @@ int FtpNetworkTransaction::ProcessResponseTYPE( return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); case ERROR_CLASS_PERMANENT_ERROR: return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -972,9 +979,6 @@ int FtpNetworkTransaction::ProcessResponseEPSV( use_epsv_ = false; next_state_ = STATE_CTRL_WRITE_PASV; return OK; - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -1009,9 +1013,6 @@ int FtpNetworkTransaction::ProcessResponsePASV( return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); case ERROR_CLASS_PERMANENT_ERROR: return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } @@ -1045,9 +1046,6 @@ int FtpNetworkTransaction::ProcessResponseRETR( return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); case ERROR_CLASS_PERMANENT_ERROR: return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; @@ -1091,9 +1089,6 @@ int FtpNetworkTransaction::ProcessResponseSIZE( return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); } break; - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } // If the resource is known beforehand to be a file, RETR should be issued, @@ -1137,9 +1132,6 @@ int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) { return ProcessResponseCWDNotADirectory(); return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; @@ -1197,9 +1189,6 @@ int FtpNetworkTransaction::ProcessResponseLIST( return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); case ERROR_CLASS_PERMANENT_ERROR: return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code)); - default: - NOTREACHED(); - return Stop(ERR_UNEXPECTED); } return OK; } diff --git a/chromium/net/ftp/ftp_network_transaction_unittest.cc b/chromium/net/ftp/ftp_network_transaction_unittest.cc index 3c75f42fda8..33ae12385d2 100644 --- a/chromium/net/ftp/ftp_network_transaction_unittest.cc +++ b/chromium/net/ftp/ftp_network_transaction_unittest.cc @@ -109,13 +109,13 @@ class FtpSocketDataProvider : public SocketDataProvider { "227 Entering Extended Passive Mode (|||31744|)\r\n"); case PRE_LIST_PASV: return Verify("PASV\r\n", data, PRE_LIST, - "227 Entering Passive Mode 127,0,0,1,123,456\r\n"); + "227 Entering Passive Mode 127,0,0,1,123,123\r\n"); case PRE_RETR_EPSV: return Verify("EPSV\r\n", data, PRE_RETR, "227 Entering Extended Passive Mode (|||31744|)\r\n"); case PRE_RETR_PASV: return Verify("PASV\r\n", data, PRE_RETR, - "227 Entering Passive Mode 127,0,0,1,123,456\r\n"); + "227 Entering Passive Mode 127,0,0,1,123,123\r\n"); case PRE_NOPASV: // Use unallocated 599 FTP error code to make sure it falls into the // generic ERR_FTP_FAILED bucket. @@ -841,15 +841,33 @@ class FtpSocketDataProviderCloseConnection : public FtpSocketDataProvider { DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderCloseConnection); }; +class BorkedFtpSocketDataProvider : public FtpSocketDataProvider { + public: + BorkedFtpSocketDataProvider() {} + ~BorkedFtpSocketDataProvider() override {} + + MockWriteResult OnWrite(const std::string& data) override { + switch (state()) { + case PRE_USER: + return Verify("USER anonymous\r\n", data, PRE_PASSWD, "957 Spam\r\n"); + default: + return FtpSocketDataProvider::OnWrite(data); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(BorkedFtpSocketDataProvider); +}; + class FtpNetworkTransactionTest : public PlatformTest, public ::testing::WithParamInterface<int> { public: - FtpNetworkTransactionTest() - : host_resolver_(new MockHostResolver), - transaction_(host_resolver_.get(), &mock_socket_factory_) { + FtpNetworkTransactionTest() : host_resolver_(new MockHostResolver) { + SetUpTransaction(); + scoped_refptr<RuleBasedHostResolverProc> rules( - new RuleBasedHostResolverProc(NULL)); + new RuleBasedHostResolverProc(nullptr)); if (GetFamily() == AF_INET) { rules->AddIPLiteralRule("*", "127.0.0.1", "127.0.0.1"); } else if (GetFamily() == AF_INET6) { @@ -860,6 +878,15 @@ class FtpNetworkTransactionTest host_resolver_->set_rules(rules.get()); } + // Sets up an FtpNetworkTransaction and MocketClientSocketFactory, replacing + // the default one. Only needs to be called if a test runs multiple + // transactions. + void SetUpTransaction() { + mock_socket_factory_.reset(new MockClientSocketFactory()); + transaction_.reset(new FtpNetworkTransaction(host_resolver_.get(), + mock_socket_factory_.get())); + } + protected: // Accessor to make code refactoring-friendly, e.g. when we change the way // parameters are passed (like more parameters). @@ -879,7 +906,7 @@ class FtpNetworkTransactionTest // Expect EPSV usage for non-IPv4 control connections. ctrl_socket->set_use_epsv((GetFamily() != AF_INET)); - mock_socket_factory_.AddSocketDataProvider(ctrl_socket); + mock_socket_factory_->AddSocketDataProvider(ctrl_socket); std::string mock_data("mock-data"); MockRead data_reads[] = { @@ -892,27 +919,26 @@ class FtpNetworkTransactionTest std::unique_ptr<StaticSocketDataProvider> data_socket( new StaticSocketDataProvider(data_reads, arraysize(data_reads), NULL, 0)); - mock_socket_factory_.AddSocketDataProvider(data_socket.get()); + mock_socket_factory_->AddSocketDataProvider(data_socket.get()); FtpRequestInfo request_info = GetRequestInfo(request); - EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); + EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState()); ASSERT_EQ(ERR_IO_PENDING, - transaction_.Start(&request_info, callback_.callback(), - NetLogWithSource())); - EXPECT_NE(LOAD_STATE_IDLE, transaction_.GetLoadState()); + transaction_->Start(&request_info, callback_.callback(), + NetLogWithSource())); + EXPECT_NE(LOAD_STATE_IDLE, transaction_->GetLoadState()); ASSERT_EQ(expected_result, callback_.WaitForResult()); if (expected_result == OK) { scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize)); memset(io_buffer->data(), 0, kBufferSize); - ASSERT_EQ(ERR_IO_PENDING, - transaction_.Read(io_buffer.get(), kBufferSize, - callback_.callback())); + ASSERT_EQ(ERR_IO_PENDING, transaction_->Read(io_buffer.get(), kBufferSize, + callback_.callback())); ASSERT_EQ(static_cast<int>(mock_data.length()), callback_.WaitForResult()); EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length())); // Do another Read to detect that the data socket is now closed. - int rv = transaction_.Read(io_buffer.get(), kBufferSize, - callback_.callback()); + int rv = transaction_->Read(io_buffer.get(), kBufferSize, + callback_.callback()); if (rv == ERR_IO_PENDING) { EXPECT_EQ(0, callback_.WaitForResult()); } else { @@ -920,7 +946,7 @@ class FtpNetworkTransactionTest } } EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state()); - EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); + EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState()); } void TransactionFailHelper(FtpSocketDataProvider* ctrl_socket, @@ -934,8 +960,8 @@ class FtpNetworkTransactionTest } std::unique_ptr<MockHostResolver> host_resolver_; - MockClientSocketFactory mock_socket_factory_; - FtpNetworkTransaction transaction_; + std::unique_ptr<MockClientSocketFactory> mock_socket_factory_; + std::unique_ptr<FtpNetworkTransaction> transaction_; TestCompletionCallback callback_; }; @@ -946,12 +972,12 @@ TEST_P(FtpNetworkTransactionTest, FailedLookup) { rules->AddSimulatedFailure("badhost"); host_resolver_->set_rules(rules.get()); - EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); + EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState()); ASSERT_EQ(ERR_IO_PENDING, - transaction_.Start(&request_info, callback_.callback(), - NetLogWithSource())); + transaction_->Start(&request_info, callback_.callback(), + NetLogWithSource())); ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_NAME_NOT_RESOLVED)); - EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState()); + EXPECT_EQ(LOAD_STATE_IDLE, transaction_->GetLoadState()); } // Check that when determining the host, the square brackets decorating IPv6 @@ -977,27 +1003,27 @@ TEST_P(FtpNetworkTransactionTest, DirectoryTransaction) { FtpSocketDataProviderDirectoryListing ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); - EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); - EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_TRUE(transaction_->GetResponseInfo()->is_directory_listing); + EXPECT_EQ(-1, transaction_->GetResponseInfo()->expected_content_size); EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1", - transaction_.GetResponseInfo()->socket_address.host()); - EXPECT_EQ(21, transaction_.GetResponseInfo()->socket_address.port()); + transaction_->GetResponseInfo()->socket_address.host()); + EXPECT_EQ(21, transaction_->GetResponseInfo()->socket_address.port()); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionWithPasvFallback) { FtpSocketDataProviderDirectoryListingWithPasvFallback ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host", OK); - EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); - EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_TRUE(transaction_->GetResponseInfo()->is_directory_listing); + EXPECT_EQ(-1, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionWithTypecode) { FtpSocketDataProviderDirectoryListing ctrl_socket; ExecuteTransaction(&ctrl_socket, "ftp://host/;type=d", OK); - EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing); - EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_TRUE(transaction_->GetResponseInfo()->is_directory_listing); + EXPECT_EQ(-1, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) { @@ -1054,10 +1080,10 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransaction) { ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); // We pass an artificial value of 18 as a response to the SIZE command. - EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size); EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1", - transaction_.GetResponseInfo()->socket_address.host()); - EXPECT_EQ(21, transaction_.GetResponseInfo()->socket_address.port()); + transaction_->GetResponseInfo()->socket_address.host()); + EXPECT_EQ(21, transaction_->GetResponseInfo()->socket_address.port()); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) { @@ -1065,7 +1091,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) { ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); // We pass an artificial value of 18 as a response to the SIZE command. - EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) { @@ -1074,7 +1100,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) { ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=a", OK); // We pass an artificial value of 18 as a response to the SIZE command. - EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) { @@ -1082,7 +1108,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) { ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=i", OK); // We pass an artificial value of 18 as a response to the SIZE command. - EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) { @@ -1161,9 +1187,37 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort4) { ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } +TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort1) { + // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. + FtpSocketDataProviderEvilPasv ctrl_socket( + "227 Portscan (127,0,0,1,256,100)\r\n", FtpSocketDataProvider::PRE_QUIT); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); +} + +TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort2) { + // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. + FtpSocketDataProviderEvilPasv ctrl_socket( + "227 Portscan (127,0,0,1,100,256)\r\n", FtpSocketDataProvider::PRE_QUIT); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); +} + +TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort3) { + // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. + FtpSocketDataProviderEvilPasv ctrl_socket( + "227 Portscan (127,0,0,1,-100,100)\r\n", FtpSocketDataProvider::PRE_QUIT); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); +} + +TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvInvalidPort4) { + // Unsafe. 8 * 256 + 1 = 2049, which is used by nfs. + FtpSocketDataProviderEvilPasv ctrl_socket( + "227 Portscan (127,0,0,1,100,-100)\r\n", FtpSocketDataProvider::PRE_QUIT); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); +} + TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) { FtpSocketDataProviderEvilPasv ctrl_socket( - "227 Portscan (10,1,2,3,123,456)\r\n", FtpSocketDataProvider::PRE_RETR); + "227 Portscan (10,1,2,3,123,123)\r\n", FtpSocketDataProvider::PRE_RETR); ctrl_socket.set_use_epsv(GetFamily() != AF_INET); std::string mock_data("mock-data"); MockRead data_reads[] = { @@ -1172,21 +1226,21 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafeHost) { StaticSocketDataProvider data_socket1; StaticSocketDataProvider data_socket2(data_reads, arraysize(data_reads), NULL, 0); - mock_socket_factory_.AddSocketDataProvider(&ctrl_socket); - mock_socket_factory_.AddSocketDataProvider(&data_socket1); - mock_socket_factory_.AddSocketDataProvider(&data_socket2); + mock_socket_factory_->AddSocketDataProvider(&ctrl_socket); + mock_socket_factory_->AddSocketDataProvider(&data_socket1); + mock_socket_factory_->AddSocketDataProvider(&data_socket2); FtpRequestInfo request_info = GetRequestInfo("ftp://host/file"); // Start the transaction. ASSERT_EQ(ERR_IO_PENDING, - transaction_.Start(&request_info, callback_.callback(), - NetLogWithSource())); + transaction_->Start(&request_info, callback_.callback(), + NetLogWithSource())); ASSERT_THAT(callback_.WaitForResult(), IsOk()); // The transaction fires the callback when we can start reading data. That // means that the data socket should be open. MockTCPClientSocket* data_socket = - static_cast<MockTCPClientSocket*>(transaction_.data_socket_.get()); + static_cast<MockTCPClientSocket*>(transaction_->data_socket_.get()); ASSERT_TRUE(data_socket); ASSERT_TRUE(data_socket->IsConnected()); @@ -1291,6 +1345,16 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort4) { ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT); } +TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvInvalidPort) { + // This test makes no sense for IPv4 connections (we don't use EPSV there). + if (GetFamily() == AF_INET) + return; + + FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||4294973296|)\r\n", + FtpSocketDataProvider::PRE_QUIT); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); +} + TEST_P(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvWeirdSep) { // This test makes no sense for IPv4 connections (we don't use EPSV there). if (GetFamily() == AF_INET) @@ -1351,7 +1415,7 @@ TEST_P(FtpNetworkTransactionTest, ExecuteTransaction(&ctrl_socket, "ftp://host/foo%2f..%2fbar%5c", OK); // We pass an artificial value of 18 as a response to the SIZE command. - EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size); + EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size); } TEST_P(FtpNetworkTransactionTest, EvilRestartUser) { @@ -1359,13 +1423,13 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartUser) { ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD, FtpSocketDataProvider::PRE_QUIT, "530 Login authentication failed\r\n"); - mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1); + mock_socket_factory_->AddSocketDataProvider(&ctrl_socket1); FtpRequestInfo request_info = GetRequestInfo("ftp://host/file"); ASSERT_EQ(ERR_IO_PENDING, - transaction_.Start(&request_info, callback_.callback(), - NetLogWithSource())); + transaction_->Start(&request_info, callback_.callback(), + NetLogWithSource())); ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED)); MockRead ctrl_reads[] = { @@ -1378,12 +1442,11 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartUser) { }; StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads), ctrl_writes, arraysize(ctrl_writes)); - mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2); + mock_socket_factory_->AddSocketDataProvider(&ctrl_socket2); ASSERT_EQ(ERR_IO_PENDING, - transaction_.RestartWithAuth( - AuthCredentials( - base::ASCIIToUTF16("foo\nownz0red"), - base::ASCIIToUTF16("innocent")), + transaction_->RestartWithAuth( + AuthCredentials(base::ASCIIToUTF16("foo\nownz0red"), + base::ASCIIToUTF16("innocent")), callback_.callback())); EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_MALFORMED_IDENTITY)); } @@ -1393,13 +1456,13 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartPassword) { ctrl_socket1.InjectFailure(FtpSocketDataProvider::PRE_PASSWD, FtpSocketDataProvider::PRE_QUIT, "530 Login authentication failed\r\n"); - mock_socket_factory_.AddSocketDataProvider(&ctrl_socket1); + mock_socket_factory_->AddSocketDataProvider(&ctrl_socket1); FtpRequestInfo request_info = GetRequestInfo("ftp://host/file"); ASSERT_EQ(ERR_IO_PENDING, - transaction_.Start(&request_info, callback_.callback(), - NetLogWithSource())); + transaction_->Start(&request_info, callback_.callback(), + NetLogWithSource())); ASSERT_THAT(callback_.WaitForResult(), IsError(ERR_FTP_FAILED)); MockRead ctrl_reads[] = { @@ -1414,9 +1477,9 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartPassword) { }; StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads), ctrl_writes, arraysize(ctrl_writes)); - mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2); + mock_socket_factory_->AddSocketDataProvider(&ctrl_socket2); ASSERT_EQ(ERR_IO_PENDING, - transaction_.RestartWithAuth( + transaction_->RestartWithAuth( AuthCredentials(base::ASCIIToUTF16("innocent"), base::ASCIIToUTF16("foo\nownz0red")), callback_.callback())); @@ -1446,7 +1509,7 @@ TEST_P(FtpNetworkTransactionTest, DownloadTransactionBigSize) { FtpSocketDataProvider::PRE_CWD); ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK); EXPECT_EQ(3204427776LL, - transaction_.GetResponseInfo()->expected_content_size); + transaction_->GetResponseInfo()->expected_content_size); } // Regression test for http://crbug.com/25023. @@ -1660,6 +1723,91 @@ TEST_P(FtpNetworkTransactionTest, ZeroLengthDirInPWD) { OK); } +TEST_P(FtpNetworkTransactionTest, UnexpectedInitiatedResponseForDirectory) { + // The states for a directory listing where an initiated response will cause + // an error. Includes all commands sent on the directory listing path, except + // CWD, SIZE, LIST, and QUIT commands. + FtpSocketDataProvider::State kFailingStates[] = { + FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_PASSWD, + FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, + FtpSocketDataProvider::PRE_TYPE, + GetFamily() != AF_INET ? FtpSocketDataProvider::PRE_LIST_EPSV + : FtpSocketDataProvider::PRE_LIST_PASV, + FtpSocketDataProvider::PRE_CWD, + }; + + for (FtpSocketDataProvider::State state : kFailingStates) { + SetUpTransaction(); + FtpSocketDataProviderDirectoryListing ctrl_socket; + ctrl_socket.InjectFailure(state, FtpSocketDataProvider::PRE_QUIT, + "157 Foo\r\n"); + ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); + } +} + +TEST_P(FtpNetworkTransactionTest, UnexpectedInitiatedResponseForFile) { + // The states for a download where an initiated response will cause an error. + // Includes all commands sent on the file download path, except CWD, SIZE, and + // QUIT commands. + const FtpSocketDataProvider::State kFailingStates[] = { + FtpSocketDataProvider::PRE_USER, FtpSocketDataProvider::PRE_PASSWD, + FtpSocketDataProvider::PRE_SYST, FtpSocketDataProvider::PRE_PWD, + FtpSocketDataProvider::PRE_TYPE, + GetFamily() != AF_INET ? FtpSocketDataProvider::PRE_RETR_EPSV + : FtpSocketDataProvider::PRE_RETR_PASV, + FtpSocketDataProvider::PRE_CWD}; + + for (FtpSocketDataProvider::State state : kFailingStates) { + LOG(ERROR) << "??: " << state; + SetUpTransaction(); + FtpSocketDataProviderFileDownload ctrl_socket; + ctrl_socket.InjectFailure(state, FtpSocketDataProvider::PRE_QUIT, + "157 Foo\r\n"); + ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE); + } +} + +// Make sure that receiving extra unexpected responses correctly results in +// sending a QUIT message, without triggering a DCHECK. +TEST_P(FtpNetworkTransactionTest, ExtraResponses) { + FtpSocketDataProviderDirectoryListing ctrl_socket; + ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_TYPE, + FtpSocketDataProvider::PRE_QUIT, + "157 Foo\r\n" + "157 Bar\r\n" + "157 Trombones\r\n"); + ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); +} + +// Make sure that receiving extra unexpected responses to a QUIT message +// correctly results in ending the transaction with an error, without triggering +// a DCHECK. +TEST_P(FtpNetworkTransactionTest, ExtraQuitResponses) { + FtpSocketDataProviderDirectoryListing ctrl_socket; + ctrl_socket.InjectFailure(FtpSocketDataProvider::PRE_QUIT, + FtpSocketDataProvider::QUIT, + "221 Foo\r\n" + "221 Bar\r\n" + "221 Trombones\r\n"); + ExecuteTransaction(&ctrl_socket, "ftp://host/", ERR_INVALID_RESPONSE); +} + +TEST_P(FtpNetworkTransactionTest, InvalidRemoteDirectory) { + FtpSocketDataProviderFileDownload ctrl_socket; + TransactionFailHelper( + &ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, + FtpSocketDataProvider::PRE_QUIT, + "257 \"foo\rbar\" is your current location\r\n", ERR_INVALID_RESPONSE); +} + +TEST_P(FtpNetworkTransactionTest, InvalidRemoteDirectory2) { + FtpSocketDataProviderFileDownload ctrl_socket; + TransactionFailHelper( + &ctrl_socket, "ftp://host/file", FtpSocketDataProvider::PRE_PWD, + FtpSocketDataProvider::PRE_QUIT, + "257 \"foo\nbar\" is your current location\r\n", ERR_INVALID_RESPONSE); +} + INSTANTIATE_TEST_CASE_P(FTP, FtpNetworkTransactionTest, ::testing::Values(AF_INET, AF_INET6)); diff --git a/chromium/net/ftp/ftp_util.cc b/chromium/net/ftp/ftp_util.cc index 00b23b7c10b..a66845300f4 100644 --- a/chromium/net/ftp/ftp_util.cc +++ b/chromium/net/ftp/ftp_util.cc @@ -45,12 +45,12 @@ std::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) { // It's an absolute path. if (tokens.empty()) { - DCHECK_EQ(1U, unix_path.length()); + // It's just "/" or a series of slashes, which all mean the same thing. return "[]"; } if (tokens.size() == 1) - return unix_path.substr(1); // Drop the leading slash. + return tokens.front(); // Return without leading slashes. std::string result(tokens[0] + ":["); if (tokens.size() == 2) { @@ -272,7 +272,7 @@ bool FtpUtil::LsDateListingToTime(const base::string16& month, // Guess the year. base::Time::Exploded current_exploded; - current_time.LocalExplode(¤t_exploded); + current_time.UTCExplode(¤t_exploded); // If it's not possible for the parsed date to be in the current year, // use the previous year. @@ -285,9 +285,8 @@ bool FtpUtil::LsDateListingToTime(const base::string16& month, } } - // We don't know the time zone of the listing, so just use local time. - *result = base::Time::FromLocalExploded(time_exploded); - return true; + // We don't know the time zone of the listing, so just use UTC. + return base::Time::FromUTCExploded(time_exploded, result); } // static @@ -348,9 +347,8 @@ bool FtpUtil::WindowsDateListingToTime(const base::string16& date, } } - // We don't know the time zone of the server, so just use local time. - *result = base::Time::FromLocalExploded(time_exploded); - return true; + // We don't know the time zone of the server, so just use UTC. + return base::Time::FromUTCExploded(time_exploded, result); } // static diff --git a/chromium/net/ftp/ftp_util_unittest.cc b/chromium/net/ftp/ftp_util_unittest.cc index a6f2ff019ef..8342fb4e534 100644 --- a/chromium/net/ftp/ftp_util_unittest.cc +++ b/chromium/net/ftp/ftp_util_unittest.cc @@ -34,6 +34,11 @@ TEST(FtpUtilTest, UnixFilePathToVMS) { { "a/b", "[.a]b" }, { "a/b/c", "[.a.b]c" }, { "a/b/c/d", "[.a.b.c]d" }, + // Extra slashes shouldn't matter. + { "/////", "[]" }, + { "/////a", "a" }, + { "//a//b///c", "a:[b]c" }, + { "a//b///c", "[.a.b]c" }, }; for (size_t i = 0; i < arraysize(kTestCases); i++) { EXPECT_EQ(kTestCases[i].expected_output, @@ -47,26 +52,30 @@ TEST(FtpUtilTest, UnixDirectoryPathToVMS) { const char* input; const char* expected_output; } kTestCases[] = { - { "", "" }, - { "/", "" }, - { "/a", "a:[000000]" }, - { "/a/", "a:[000000]" }, - { "/a/b", "a:[b]" }, - { "/a/b/", "a:[b]" }, - { "/a/b/c", "a:[b.c]" }, - { "/a/b/c/", "a:[b.c]" }, - { "/a/b/c/d", "a:[b.c.d]" }, - { "/a/b/c/d/", "a:[b.c.d]" }, - { "/a/b/c/d/e", "a:[b.c.d.e]" }, - { "/a/b/c/d/e/", "a:[b.c.d.e]" }, - { "a", "[.a]" }, - { "a/", "[.a]" }, - { "a/b", "[.a.b]" }, - { "a/b/", "[.a.b]" }, - { "a/b/c", "[.a.b.c]" }, - { "a/b/c/", "[.a.b.c]" }, - { "a/b/c/d", "[.a.b.c.d]" }, - { "a/b/c/d/", "[.a.b.c.d]" }, + { "", "" }, + { "/", "" }, + { "/a", "a:[000000]" }, + { "/a/", "a:[000000]" }, + { "/a/b", "a:[b]" }, + { "/a/b/", "a:[b]" }, + { "/a/b/c", "a:[b.c]" }, + { "/a/b/c/", "a:[b.c]" }, + { "/a/b/c/d", "a:[b.c.d]" }, + { "/a/b/c/d/", "a:[b.c.d]" }, + { "/a/b/c/d/e", "a:[b.c.d.e]" }, + { "/a/b/c/d/e/", "a:[b.c.d.e]" }, + { "a", "[.a]" }, + { "a/", "[.a]" }, + { "a/b", "[.a.b]" }, + { "a/b/", "[.a.b]" }, + { "a/b/c", "[.a.b.c]" }, + { "a/b/c/", "[.a.b.c]" }, + { "a/b/c/d", "[.a.b.c.d]" }, + { "a/b/c/d/", "[.a.b.c.d]" }, + // Extra slashes shouldn't matter. + { "/////", "" }, + { "//a//b///c//", "a:[b.c]" }, + { "a//b///c//", "[.a.b.c]" }, }; for (size_t i = 0; i < arraysize(kTestCases); i++) { EXPECT_EQ(kTestCases[i].expected_output, @@ -175,7 +184,7 @@ TEST(FtpUtilTest, LsDateListingToTime) { UTF8ToUTF16(kTestCases[i].rest), mock_current_time, &time)); base::Time::Exploded time_exploded; - time.LocalExplode(&time_exploded); + time.UTCExplode(&time_exploded); EXPECT_EQ(kTestCases[i].expected_year, time_exploded.year); EXPECT_EQ(kTestCases[i].expected_month, time_exploded.month); EXPECT_EQ(kTestCases[i].expected_day_of_month, time_exploded.day_of_month); @@ -215,7 +224,7 @@ TEST(FtpUtilTest, WindowsDateListingToTime) { &time)); base::Time::Exploded time_exploded; - time.LocalExplode(&time_exploded); + time.UTCExplode(&time_exploded); EXPECT_EQ(kTestCases[i].expected_year, time_exploded.year); EXPECT_EQ(kTestCases[i].expected_month, time_exploded.month); EXPECT_EQ(kTestCases[i].expected_day_of_month, time_exploded.day_of_month); |