Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017 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 #include "net/http/http_auth_handler_ntlm.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/base64.h" | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/strings/utf_string_conversions.h" | |
| 13 #include "net/base/net_errors.h" | |
| 14 #include "net/base/test_completion_callback.h" | |
| 15 #include "net/dns/mock_host_resolver.h" | |
| 16 #include "net/http/http_auth_challenge_tokenizer.h" | |
| 17 #include "net/http/http_request_info.h" | |
| 18 #include "net/http/mock_allow_http_auth_preferences.h" | |
| 19 #include "net/log/net_log_with_source.h" | |
| 20 #include "net/ssl/ssl_info.h" | |
| 21 #include "net/test/gtest_util.h" | |
| 22 #include "testing/gmock/include/gmock/gmock.h" | |
| 23 #include "testing/gtest/include/gtest/gtest.h" | |
| 24 #include "testing/platform_test.h" | |
| 25 | |
| 26 using net::test::IsError; | |
| 27 using net::test::IsOk; | |
| 28 | |
| 29 namespace net { | |
| 30 | |
| 31 #if defined(NTLM_PORTABLE) | |
| 32 | |
| 33 const char NTLM_SIGNATURE[] = "NTLMSSP"; | |
| 34 | |
| 35 class HttpAuthHandlerNtlmPortableTest : public PlatformTest { | |
| 36 public: | |
| 37 void SetUp() override { | |
| 38 http_auth_preferences_.reset(new MockAllowHttpAuthPreferences()); | |
| 39 factory_.reset(new HttpAuthHandlerNTLM::Factory()); | |
| 40 factory_->set_http_auth_preferences(http_auth_preferences_.get()); | |
| 41 creds_ = AuthCredentials(base::ASCIIToUTF16("someuser"), | |
| 42 base::ASCIIToUTF16("badpassword")); | |
|
Ryan Sleevi
2017/05/09 20:49:27
Any reason for the SetUp vs CTOR?
(See https://gi
zentaro
2017/05/15 20:48:38
Nope. I just copied from the Negotiate one as a st
| |
| 43 } | |
| 44 | |
| 45 int CreateHandler() { | |
| 46 GURL gurl("foo.com"); | |
|
Ryan Sleevi
2017/05/09 20:49:27
Since you're creating a GURL, let's use a proper U
zentaro
2017/05/15 20:48:37
Done.
| |
| 47 SSLInfo null_ssl_info; | |
| 48 | |
| 49 return factory_->CreateAuthHandlerFromString( | |
| 50 "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(), | |
| 51 &auth_handler_); | |
| 52 } | |
| 53 | |
| 54 std::string CreateType2Token(const char* message, size_t message_len) { | |
|
Ryan Sleevi
2017/05/09 20:49:27
Consider using base::StringPiece ?
zentaro
2017/05/15 20:48:38
Done.
| |
| 55 std::string output; | |
| 56 std::string input(message, message_len); | |
| 57 base::Base64Encode(input, &output); | |
| 58 | |
| 59 return "NTLM " + output; | |
| 60 } | |
| 61 | |
| 62 void HandleAnotherChallenge(std::string challenge) { | |
|
Ryan Sleevi
2017/05/09 20:49:27
Pass this by const-ref? Or did you mean to explici
zentaro
2017/05/15 20:48:37
Done.
| |
| 63 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
| 64 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
| 65 GetAuthHandler()->HandleAnotherChallenge(&tokenizer)); | |
| 66 } | |
| 67 | |
| 68 int GenerateAuthToken(std::string* token) { | |
| 69 TestCompletionCallback callback; | |
| 70 HttpRequestInfo request_info; | |
| 71 return callback.GetResult(GetAuthHandler()->GenerateAuthToken( | |
| 72 GetCreds(), &request_info, callback.callback(), token)); | |
|
Ryan Sleevi
2017/05/09 20:49:27
There's no way to wait for the callback's result s
zentaro
2017/05/15 20:48:37
Looks like TestCompletionCallback::GetResult check
| |
| 73 } | |
| 74 | |
| 75 int GenerateAuthToken() { | |
|
Ryan Sleevi
2017/05/09 20:49:27
Why this overload? This is a fairly surprising nam
zentaro
2017/05/15 20:48:37
As above I think this runs synchronously. The over
| |
| 76 std::string token; | |
| 77 return GenerateAuthToken(&token); | |
| 78 } | |
| 79 | |
| 80 AuthCredentials* GetCreds() { return &creds_; } | |
| 81 | |
| 82 HttpAuthHandlerNTLM* GetAuthHandler() { | |
| 83 return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get()); | |
| 84 } | |
| 85 | |
| 86 static void MockRandom(uint8_t* output, size_t n) { | |
| 87 static const uint8_t bytes[] = {0x55, 0x29, 0x66, 0x26, | |
| 88 0x6b, 0x9c, 0x73, 0x54}; | |
| 89 static size_t current_byte = 0; | |
| 90 for (size_t i = 0; i < n; ++i) { | |
| 91 output[i] = bytes[current_byte++]; | |
| 92 current_byte %= arraysize(bytes); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 static std::string MockGetHostName() { return "MYHOSTNAME"; } | |
| 97 | |
| 98 private: | |
| 99 AuthCredentials creds_; | |
| 100 std::unique_ptr<HttpAuthHandler> auth_handler_; | |
| 101 std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_; | |
| 102 std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_; | |
| 103 }; | |
| 104 | |
| 105 TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) { | |
| 106 EXPECT_EQ(OK, CreateHandler()); | |
| 107 ASSERT_TRUE(GetAuthHandler() != nullptr); | |
| 108 } | |
| 109 | |
| 110 TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) { | |
| 111 EXPECT_EQ(OK, CreateHandler()); | |
| 112 EXPECT_FALSE(GetAuthHandler()->AllowsDefaultCredentials()); | |
| 113 } | |
| 114 | |
| 115 TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) { | |
| 116 EXPECT_EQ(OK, CreateHandler()); | |
| 117 EXPECT_TRUE(GetAuthHandler()->AllowsExplicitCredentials()); | |
| 118 } | |
| 119 | |
| 120 TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) { | |
| 121 EXPECT_EQ(OK, CreateHandler()); | |
| 122 | |
| 123 std::string token; | |
| 124 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
| 125 // The type 1 message generated is always the same. The only variable | |
| 126 // part of the message is the flags and Chrome always offers the same | |
| 127 // set of flags. | |
| 128 EXPECT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=", token); | |
| 129 } | |
| 130 | |
| 131 TEST_F(HttpAuthHandlerNtlmPortableTest, ValidType2Input) { | |
| 132 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
| 133 MockGetHostName); | |
| 134 EXPECT_EQ(OK, CreateHandler()); | |
| 135 | |
| 136 // This is a real type 2 message sent by a Windows Server in response to | |
| 137 // Chrome's type 1 message. | |
|
Ryan Sleevi
2017/05/09 20:49:27
How was this computed? What are the values? How ca
zentaro
2017/05/15 20:48:38
It was just copied from a real transaction. I was
| |
| 138 std::string challenge_str = | |
| 139 "NTLM " | |
| 140 "TlRMTVNTUAACAAAADAAMADgAAAAFgokCXziKeNIPIDYAAAAAAAAAAIYAhgBEAAAABgOAJQAA" | |
| 141 "AA9aAEUATgBEAE8ATQACAAwAWgBFAE4ARABPAE0AAQAMAFoARQBOAEQAQwAxAAQAFAB6AGUA" | |
| 142 "bgBkAG8AbQAuAGwAbwBjAAMAIgBaAGUAbgBEAEMAMQAuAHoAZQBuAGQAbwBtAC4AbABvAGMA" | |
| 143 "BQAUAHoAZQBuAGQAbwBtAC4AbABvAGMABwAIAN6N+IxGwNIBAAAAAA=="; | |
| 144 std::string token; | |
| 145 HandleAnotherChallenge(challenge_str); | |
| 146 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
| 147 | |
| 148 // Type 3 message based on mocked RNG data. | |
| 149 EXPECT_EQ( | |
| 150 "NTLM " | |
| 151 "TlRMTVNTUAADAAAAGAAYAGQAAAAYABgAfAAAAAAAAABAAAAAEAAQAEAAAAAUABQAUAAAAAAA" | |
| 152 "AAAAAAAABYIIAHMAbwBtAGUAdQBzAGUAcgBNAFkASABPAFMAVABOAEEATQBFAFUpZiZrnHNU" | |
| 153 "AAAAAAAAAAAAAAAAAAAAAOYv6VCd3XSbN+S4BO5kmGWvVzlFBGnJOg==", | |
| 154 token); | |
| 155 } | |
| 156 | |
| 157 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageTooShort) { | |
| 158 EXPECT_EQ(OK, CreateHandler()); | |
| 159 | |
| 160 // Fail because the minimum size valid message is 32 bytes. | |
| 161 char raw[31]; | |
| 162 memset(raw, 0, sizeof(raw)); | |
| 163 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 164 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
| 165 } | |
| 166 | |
| 167 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageNoSig) { | |
| 168 EXPECT_EQ(OK, CreateHandler()); | |
| 169 | |
| 170 // Fail because the first bytes don't match "NTLMSSP\0" | |
| 171 char raw[32]; | |
| 172 memset(raw, 0, sizeof(raw)); | |
| 173 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 174 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
| 175 } | |
| 176 | |
| 177 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2WrongMessageType) { | |
| 178 EXPECT_EQ(OK, CreateHandler()); | |
| 179 | |
| 180 // Fail because the message type (starting byte 8) should be 0x00000002. | |
| 181 char raw[32]; | |
| 182 memset(raw, 0, sizeof(raw)); | |
| 183 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 184 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 185 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
| 186 } | |
| 187 | |
| 188 TEST_F(HttpAuthHandlerNtlmPortableTest, MinimalStructurallyValidType2) { | |
| 189 EXPECT_EQ(OK, CreateHandler()); | |
| 190 | |
| 191 char raw[32]; | |
| 192 memset(raw, 0, sizeof(raw)); | |
| 193 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 194 // Mark as a type 2 message. | |
| 195 raw[8] = 2; | |
| 196 | |
| 197 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 198 EXPECT_EQ(OK, GenerateAuthToken()); | |
| 199 } | |
| 200 | |
| 201 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithNoTargetName) { | |
| 202 EXPECT_EQ(OK, CreateHandler()); | |
| 203 | |
| 204 char raw[32]; | |
| 205 memset(raw, 0, sizeof(raw)); | |
| 206 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 207 // Mark as a type 2 message. | |
| 208 raw[8] = 2; | |
| 209 // Point the offset to the end of the buffer. | |
| 210 raw[16] = 32; | |
| 211 | |
| 212 // Although the MinimalStructurallyValidType2 message with both | |
| 213 // length and offset equal zero is not forbidden by the spec, the | |
| 214 // spec (2.2.1.2) states that the length SHOULD be 0 and the | |
| 215 // offset SHOULD be where the payload would be if it was present. | |
| 216 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 217 EXPECT_EQ(OK, GenerateAuthToken()); | |
| 218 } | |
| 219 | |
| 220 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithTargetName) { | |
| 221 EXPECT_EQ(OK, CreateHandler()); | |
| 222 | |
| 223 // One extra byte is provided for target name. | |
| 224 char raw[33]; | |
| 225 memset(raw, 0, sizeof(raw)); | |
| 226 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 227 // Mark as a type 2 message. | |
| 228 raw[8] = 2; | |
| 229 // The target name field is 1 byte long. | |
| 230 raw[12] = 1; | |
| 231 raw[14] = 1; | |
| 232 // Point the offset to the extra 1 byte space. | |
| 233 raw[16] = 32; | |
| 234 | |
| 235 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 236 EXPECT_EQ(OK, GenerateAuthToken()); | |
| 237 } | |
| 238 | |
| 239 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromOffset) { | |
| 240 EXPECT_EQ(OK, CreateHandler()); | |
| 241 | |
| 242 char raw[32]; | |
| 243 memset(raw, 0, sizeof(raw)); | |
| 244 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 245 // Mark as a type 2 message. | |
| 246 raw[8] = 2; | |
| 247 // Claim that the target name field is 1 byte long. | |
| 248 raw[12] = 1; | |
| 249 raw[14] = 1; | |
| 250 // Point the offset outside the message buffer. | |
| 251 raw[16] = 32; | |
| 252 | |
| 253 // The above malformed message could cause an implementation | |
| 254 // to read outside the message buffer because the offset is | |
| 255 // past the end of the message. Verify it gets rejected. | |
| 256 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 257 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
| 258 } | |
| 259 | |
| 260 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromLength) { | |
| 261 EXPECT_EQ(OK, CreateHandler()); | |
| 262 | |
| 263 // Message has 1 extra byte of space after the header for the | |
| 264 // target name. | |
| 265 char raw[33]; | |
| 266 memset(raw, 0, sizeof(raw)); | |
| 267 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
| 268 // Mark as a type 2 message. | |
| 269 raw[8] = 2; | |
| 270 // Claim that the target name field is 2 bytes long. | |
| 271 raw[12] = 2; | |
| 272 raw[14] = 2; | |
| 273 // Point the offset to the extra 1 byte space. | |
| 274 raw[16] = 32; | |
| 275 | |
| 276 // The above malformed message could cause an implementation | |
| 277 // to read outside the message buffer because the length is | |
| 278 // longer than available space. Verify it gets rejected. | |
| 279 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
| 280 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
| 281 } | |
| 282 | |
| 283 #endif // defined(NTLM_PORTABLE) | |
| 284 | |
| 285 } // namespace net | |
| OLD | NEW |