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.
5 #include "net/http/http_auth_handler_negotiate.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/test_completion_callback.h"
13 #include "net/dns/mock_host_resolver.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/mock_allow_url_security_manager.h"
16 #if defined(OS_ANDROID)
17 #include "net/android/dummy_spnego_authenticator.h"
19 #include "net/http/mock_sspi_library_win.h"
20 #elif defined(OS_POSIX)
21 #include "net/http/mock_gssapi_library_posix.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "testing/platform_test.h"
28 #if defined(OS_ANDROID)
29 typedef net::android::DummySpnegoAuthenticator MockAuthLibrary
;
31 typedef MockSSPILibrary MockAuthLibrary
;
32 #elif defined(OS_POSIX)
33 typedef test::MockGSSAPILibrary MockAuthLibrary
;
36 class HttpAuthHandlerNegotiateTest
: public PlatformTest
{
38 void SetUp() override
{
39 auth_library_
= new MockAuthLibrary();
40 resolver_
.reset(new MockHostResolver());
41 resolver_
->rules()->AddIPLiteralRule("alias", "10.0.0.2",
42 "canonical.example.com");
44 url_security_manager_
.reset(new MockAllowURLSecurityManager());
45 factory_
.reset(new HttpAuthHandlerNegotiate::Factory());
46 factory_
->set_url_security_manager(url_security_manager_
.get());
47 #if defined(OS_ANDROID)
48 std::string
* authenticator
=
49 new std::string("org.chromium.test.DummySpnegoAuthenticator");
50 factory_
->set_library(authenticator
);
51 MockAuthLibrary::EnsureTestAccountExists();
53 #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_ANDROID))
54 factory_
->set_library(auth_library_
);
56 factory_
->set_host_resolver(resolver_
.get());
59 #if defined(OS_ANDROID)
60 void TearDown() override
{ MockAuthLibrary::RemoveTestAccounts(); }
63 void SetupMocks(MockAuthLibrary
* mock_library
) {
65 security_package_
.reset(new SecPkgInfoW
);
66 memset(security_package_
.get(), 0x0, sizeof(SecPkgInfoW
));
67 security_package_
->cbMaxToken
= 1337;
68 mock_library
->ExpectQuerySecurityPackageInfo(
69 L
"Negotiate", SEC_E_OK
, security_package_
.get());
70 #elif defined(OS_POSIX)
71 // Copied from an actual transaction!
72 static const char kAuthResponse
[] =
73 "\x60\x82\x02\xCA\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x01"
74 "\x00\x6E\x82\x02\xB9\x30\x82\x02\xB5\xA0\x03\x02\x01\x05\xA1\x03"
75 "\x02\x01\x0E\xA2\x07\x03\x05\x00\x00\x00\x00\x00\xA3\x82\x01\xC1"
76 "\x61\x82\x01\xBD\x30\x82\x01\xB9\xA0\x03\x02\x01\x05\xA1\x16\x1B"
77 "\x14\x55\x4E\x49\x58\x2E\x43\x4F\x52\x50\x2E\x47\x4F\x4F\x47\x4C"
78 "\x45\x2E\x43\x4F\x4D\xA2\x2C\x30\x2A\xA0\x03\x02\x01\x01\xA1\x23"
79 "\x30\x21\x1B\x04\x68\x6F\x73\x74\x1B\x19\x6E\x69\x6E\x6A\x61\x2E"
80 "\x63\x61\x6D\x2E\x63\x6F\x72\x70\x2E\x67\x6F\x6F\x67\x6C\x65\x2E"
81 "\x63\x6F\x6D\xA3\x82\x01\x6A\x30\x82\x01\x66\xA0\x03\x02\x01\x10"
82 "\xA1\x03\x02\x01\x01\xA2\x82\x01\x58\x04\x82\x01\x54\x2C\xB1\x2B"
83 "\x0A\xA5\xFF\x6F\xEC\xDE\xB0\x19\x6E\x15\x20\x18\x0C\x42\xB3\x2C"
84 "\x4B\xB0\x37\x02\xDE\xD3\x2F\xB4\xBF\xCA\xEC\x0E\xF9\xF3\x45\x6A"
85 "\x43\xF3\x8D\x79\xBD\xCB\xCD\xB2\x2B\xB8\xFC\xD6\xB4\x7F\x09\x48"
86 "\x14\xA7\x4F\xD2\xEE\xBC\x1B\x2F\x18\x3B\x81\x97\x7B\x28\xA4\xAF"
87 "\xA8\xA3\x7A\x31\x1B\xFC\x97\xB6\xBA\x8A\x50\x50\xD7\x44\xB8\x30"
88 "\xA4\x51\x4C\x3A\x95\x6C\xA1\xED\xE2\xEF\x17\xFE\xAB\xD2\xE4\x70"
89 "\xDE\xEB\x7E\x86\x48\xC5\x3E\x19\x5B\x83\x17\xBB\x52\x26\xC0\xF3"
90 "\x38\x0F\xB0\x8C\x72\xC9\xB0\x8B\x99\x96\x18\xE1\x9E\x67\x9D\xDC"
91 "\xF5\x39\x80\x70\x35\x3F\x98\x72\x16\x44\xA2\xC0\x10\xAA\x70\xBD"
92 "\x06\x6F\x83\xB1\xF4\x67\xA4\xBD\xDA\xF7\x79\x1D\x96\xB5\x7E\xF8"
93 "\xC6\xCF\xB4\xD9\x51\xC9\xBB\xB4\x20\x3C\xDD\xB9\x2C\x38\xEA\x40"
94 "\xFB\x02\x6C\xCB\x48\x71\xE8\xF4\x34\x5B\x63\x5D\x13\x57\xBD\xD1"
95 "\x3D\xDE\xE8\x4A\x51\x6E\xBE\x4C\xF5\xA3\x84\xF7\x4C\x4E\x58\x04"
96 "\xBE\xD1\xCC\x22\xA0\x43\xB0\x65\x99\x6A\xE0\x78\x0D\xFC\xE1\x42"
97 "\xA9\x18\xCF\x55\x4D\x23\xBD\x5C\x0D\xB5\x48\x25\x47\xCC\x01\x54"
98 "\x36\x4D\x0C\x6F\xAC\xCD\x33\x21\xC5\x63\x18\x91\x68\x96\xE9\xD1"
99 "\xD8\x23\x1F\x21\xAE\x96\xA3\xBD\x27\xF7\x4B\xEF\x4C\x43\xFF\xF8"
100 "\x22\x57\xCF\x68\x6C\x35\xD5\x21\x48\x5B\x5F\x8F\xA5\xB9\x6F\x99"
101 "\xA6\xE0\x6E\xF0\xC5\x7C\x91\xC8\x0B\x8A\x4B\x4E\x80\x59\x02\xE9"
102 "\xE8\x3F\x87\x04\xA6\xD1\xCA\x26\x3C\xF0\xDA\x57\xFA\xE6\xAF\x25"
103 "\x43\x34\xE1\xA4\x06\x1A\x1C\xF4\xF5\x21\x9C\x00\x98\xDD\xF0\xB4"
104 "\x8E\xA4\x81\xDA\x30\x81\xD7\xA0\x03\x02\x01\x10\xA2\x81\xCF\x04"
105 "\x81\xCC\x20\x39\x34\x60\x19\xF9\x4C\x26\x36\x46\x99\x7A\xFD\x2B"
106 "\x50\x8B\x2D\x47\x72\x38\x20\x43\x0E\x6E\x28\xB3\xA7\x4F\x26\xF1"
107 "\xF1\x7B\x02\x63\x58\x5A\x7F\xC8\xD0\x6E\xF5\xD1\xDA\x28\x43\x1B"
108 "\x6D\x9F\x59\x64\xDE\x90\xEA\x6C\x8C\xA9\x1B\x1E\x92\x29\x24\x23"
109 "\x2C\xE3\xEA\x64\xEF\x91\xA5\x4E\x94\xE1\xDC\x56\x3A\xAF\xD5\xBC"
110 "\xC9\xD3\x9B\x6B\x1F\xBE\x40\xE5\x40\xFF\x5E\x21\xEA\xCE\xFC\xD5"
111 "\xB0\xE5\xBA\x10\x94\xAE\x16\x54\xFC\xEB\xAB\xF1\xD4\x20\x31\xCC"
112 "\x26\xFE\xBE\xFE\x22\xB6\x9B\x1A\xE5\x55\x2C\x93\xB7\x3B\xD6\x4C"
113 "\x35\x35\xC1\x59\x61\xD4\x1F\x2E\x4C\xE1\x72\x8F\x71\x4B\x0C\x39"
114 "\x80\x79\xFA\xCD\xEA\x71\x1B\xAE\x35\x41\xED\xF9\x65\x0C\x59\xF8"
115 "\xE1\x27\xDA\xD6\xD1\x20\x32\xCD\xBF\xD1\xEF\xE2\xED\xAD\x5D\xA7"
116 "\x69\xE3\x55\xF9\x30\xD3\xD4\x08\xC8\xCA\x62\xF8\x64\xEC\x9B\x92"
117 "\x1A\xF1\x03\x2E\xCC\xDC\xEB\x17\xDE\x09\xAC\xA9\x58\x86";
118 test::GssContextMockImpl
context1(
119 "localhost", // Source name
120 "example.com", // Target name
122 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
124 1, // Locally initiated
126 test::GssContextMockImpl
context2(
127 "localhost", // Source name
128 "example.com", // Target name
130 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
132 1, // Locally initiated
134 MockAuthLibrary::SecurityContextQuery queries
[] = {
135 MockAuthLibrary::SecurityContextQuery(
136 "Negotiate", // Package name
137 GSS_S_CONTINUE_NEEDED
, // Major response code
138 0, // Minor response code
140 NULL
, // Expected input token
141 kAuthResponse
), // Output token
142 MockAuthLibrary::SecurityContextQuery(
143 "Negotiate", // Package name
144 GSS_S_COMPLETE
, // Major response code
145 0, // Minor response code
147 kAuthResponse
, // Expected input token
148 kAuthResponse
) // Output token
151 for (size_t i
= 0; i
< arraysize(queries
); ++i
) {
152 mock_library
->ExpectSecurityContext(queries
[i
].expected_package
,
153 queries
[i
].response_code
,
154 queries
[i
].minor_response_code
,
155 queries
[i
].context_info
,
156 queries
[i
].expected_input_token
,
157 queries
[i
].output_token
);
159 #endif // defined(OS_POSIX)
162 #if defined(OS_POSIX)
163 void SetupErrorMocks(MockAuthLibrary
* mock_library
,
166 const gss_OID_desc kDefaultMech
= { 0, NULL
};
167 test::GssContextMockImpl
context(
168 "localhost", // Source name
169 "example.com", // Target name
171 kDefaultMech
, // Mechanism
173 1, // Locally initiated
175 MockAuthLibrary::SecurityContextQuery
query(
176 "Negotiate", // Package name
177 major_status
, // Major response code
178 minor_status
, // Minor response code
180 NULL
, // Expected input token
181 NULL
); // Output token
183 mock_library
->ExpectSecurityContext(query
.expected_package
,
185 query
.minor_response_code
,
187 query
.expected_input_token
,
191 #endif // defined(OS_POSIX)
193 int CreateHandler(bool disable_cname_lookup
, bool use_port
,
194 bool synchronous_resolve_mode
,
195 const std::string
& url_string
,
196 scoped_ptr
<HttpAuthHandlerNegotiate
>* handler
) {
197 factory_
->set_disable_cname_lookup(disable_cname_lookup
);
198 factory_
->set_use_port(use_port
);
199 resolver_
->set_synchronous_mode(synchronous_resolve_mode
);
200 GURL
gurl(url_string
);
202 // Note: This is a little tricky because CreateAuthHandlerFromString
203 // expects a scoped_ptr<HttpAuthHandler>* rather than a
204 // scoped_ptr<HttpAuthHandlerNegotiate>*. This needs to do the cast
205 // after creating the handler, and make sure that generic_handler
206 // no longer holds on to the HttpAuthHandlerNegotiate object.
207 scoped_ptr
<HttpAuthHandler
> generic_handler
;
208 int rv
= factory_
->CreateAuthHandlerFromString("Negotiate",
209 HttpAuth::AUTH_SERVER
,
215 HttpAuthHandlerNegotiate
* negotiate_handler
=
216 static_cast<HttpAuthHandlerNegotiate
*>(generic_handler
.release());
217 handler
->reset(negotiate_handler
);
221 MockAuthLibrary
* AuthLibrary() { return auth_library_
; }
225 scoped_ptr
<SecPkgInfoW
> security_package_
;
227 // |auth_library_| is passed to |factory_|, which assumes ownership of it.
228 MockAuthLibrary
* auth_library_
;
229 scoped_ptr
<MockHostResolver
> resolver_
;
230 scoped_ptr
<URLSecurityManager
> url_security_manager_
;
231 scoped_ptr
<HttpAuthHandlerNegotiate::Factory
> factory_
;
234 TEST_F(HttpAuthHandlerNegotiateTest
, DisableCname
) {
235 SetupMocks(AuthLibrary());
236 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
237 EXPECT_EQ(OK
, CreateHandler(
238 true, false, true, "http://alias:500", &auth_handler
));
240 ASSERT_TRUE(auth_handler
.get() != NULL
);
241 TestCompletionCallback callback
;
242 HttpRequestInfo request_info
;
244 EXPECT_EQ(OK
, callback
.GetResult(auth_handler
->GenerateAuthToken(
245 NULL
, &request_info
, callback
.callback(), &token
)));
247 EXPECT_EQ("HTTP/alias", auth_handler
->spn());
248 #elif defined(OS_POSIX)
249 EXPECT_EQ("HTTP@alias", auth_handler
->spn());
253 TEST_F(HttpAuthHandlerNegotiateTest
, DisableCnameStandardPort
) {
254 SetupMocks(AuthLibrary());
255 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
256 EXPECT_EQ(OK
, CreateHandler(
257 true, true, true, "http://alias:80", &auth_handler
));
258 ASSERT_TRUE(auth_handler
.get() != NULL
);
259 TestCompletionCallback callback
;
260 HttpRequestInfo request_info
;
262 EXPECT_EQ(OK
, callback
.GetResult(auth_handler
->GenerateAuthToken(
263 NULL
, &request_info
, callback
.callback(), &token
)));
265 EXPECT_EQ("HTTP/alias", auth_handler
->spn());
266 #elif defined(OS_POSIX)
267 EXPECT_EQ("HTTP@alias", auth_handler
->spn());
271 TEST_F(HttpAuthHandlerNegotiateTest
, DisableCnameNonstandardPort
) {
272 SetupMocks(AuthLibrary());
273 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
274 EXPECT_EQ(OK
, CreateHandler(
275 true, true, true, "http://alias:500", &auth_handler
));
276 ASSERT_TRUE(auth_handler
.get() != NULL
);
277 TestCompletionCallback callback
;
278 HttpRequestInfo request_info
;
280 EXPECT_EQ(OK
, callback
.GetResult(auth_handler
->GenerateAuthToken(
281 NULL
, &request_info
, callback
.callback(), &token
)));
283 EXPECT_EQ("HTTP/alias:500", auth_handler
->spn());
284 #elif defined(OS_POSIX)
285 EXPECT_EQ("HTTP@alias:500", auth_handler
->spn());
289 TEST_F(HttpAuthHandlerNegotiateTest
, CnameSync
) {
290 SetupMocks(AuthLibrary());
291 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
292 EXPECT_EQ(OK
, CreateHandler(
293 false, false, true, "http://alias:500", &auth_handler
));
294 ASSERT_TRUE(auth_handler
.get() != NULL
);
295 TestCompletionCallback callback
;
296 HttpRequestInfo request_info
;
298 EXPECT_EQ(OK
, callback
.GetResult(auth_handler
->GenerateAuthToken(
299 NULL
, &request_info
, callback
.callback(), &token
)));
301 EXPECT_EQ("HTTP/canonical.example.com", auth_handler
->spn());
302 #elif defined(OS_POSIX)
303 EXPECT_EQ("HTTP@canonical.example.com", auth_handler
->spn());
307 TEST_F(HttpAuthHandlerNegotiateTest
, CnameAsync
) {
308 SetupMocks(AuthLibrary());
309 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
310 EXPECT_EQ(OK
, CreateHandler(
311 false, false, false, "http://alias:500", &auth_handler
));
312 ASSERT_TRUE(auth_handler
.get() != NULL
);
313 TestCompletionCallback callback
;
314 HttpRequestInfo request_info
;
316 EXPECT_EQ(ERR_IO_PENDING
, auth_handler
->GenerateAuthToken(
317 NULL
, &request_info
, callback
.callback(), &token
));
318 EXPECT_EQ(OK
, callback
.WaitForResult());
320 EXPECT_EQ("HTTP/canonical.example.com", auth_handler
->spn());
321 #elif defined(OS_POSIX)
322 EXPECT_EQ("HTTP@canonical.example.com", auth_handler
->spn());
326 #if defined(OS_POSIX)
328 // This test is only for GSSAPI, as we can't use explicit credentials with
330 TEST_F(HttpAuthHandlerNegotiateTest
, ServerNotInKerberosDatabase
) {
331 SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE
, 0x96C73A07); // No server
332 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
333 EXPECT_EQ(OK
, CreateHandler(
334 false, false, false, "http://alias:500", &auth_handler
));
335 ASSERT_TRUE(auth_handler
.get() != NULL
);
336 TestCompletionCallback callback
;
337 HttpRequestInfo request_info
;
339 EXPECT_EQ(ERR_IO_PENDING
, auth_handler
->GenerateAuthToken(
340 NULL
, &request_info
, callback
.callback(), &token
));
341 EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS
, callback
.WaitForResult());
344 // This test is only for GSSAPI, as we can't use explicit credentials with
346 TEST_F(HttpAuthHandlerNegotiateTest
, NoKerberosCredentials
) {
347 SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE
, 0x96C73AC3); // No credentials
348 scoped_ptr
<HttpAuthHandlerNegotiate
> auth_handler
;
349 EXPECT_EQ(OK
, CreateHandler(
350 false, false, false, "http://alias:500", &auth_handler
));
351 ASSERT_TRUE(auth_handler
.get() != NULL
);
352 TestCompletionCallback callback
;
353 HttpRequestInfo request_info
;
355 EXPECT_EQ(ERR_IO_PENDING
, auth_handler
->GenerateAuthToken(
356 NULL
, &request_info
, callback
.callback(), &token
));
357 EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS
, callback
.WaitForResult());
360 #if defined(DLOPEN_KERBEROS)
361 TEST_F(HttpAuthHandlerNegotiateTest
, MissingGSSAPI
) {
362 scoped_ptr
<HostResolver
> host_resolver(new MockHostResolver());
363 MockAllowURLSecurityManager url_security_manager
;
364 scoped_ptr
<HttpAuthHandlerNegotiate::Factory
> negotiate_factory(
365 new HttpAuthHandlerNegotiate::Factory());
366 negotiate_factory
->set_host_resolver(host_resolver
.get());
367 negotiate_factory
->set_url_security_manager(&url_security_manager
);
368 negotiate_factory
->set_library(
369 new GSSAPISharedLibrary("/this/library/does/not/exist"));
371 GURL
gurl("http://www.example.com");
372 scoped_ptr
<HttpAuthHandler
> generic_handler
;
373 int rv
= negotiate_factory
->CreateAuthHandlerFromString(
375 HttpAuth::AUTH_SERVER
,
379 EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME
, rv
);
380 EXPECT_TRUE(generic_handler
.get() == NULL
);
382 #endif // defined(DLOPEN_KERBEROS)
384 #endif // defined(OS_POSIX)