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_gssapi_posix.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/native_library.h"
11 #include "net/base/net_errors.h"
12 #include "net/http/http_auth_challenge_tokenizer.h"
13 #include "net/http/mock_gssapi_library_posix.h"
14 #include "testing/gtest/include/gtest/gtest.h"
20 // gss_buffer_t helpers.
21 void ClearBuffer(gss_buffer_t dest
) {
25 delete [] reinterpret_cast<char*>(dest
->value
);
29 void SetBuffer(gss_buffer_t dest
, const void* src
, size_t length
) {
35 dest
->length
= length
;
37 dest
->value
= new char[length
];
38 memcpy(dest
->value
, src
, length
);
42 void CopyBuffer(gss_buffer_t dest
, const gss_buffer_t src
) {
48 SetBuffer(dest
, src
->value
, src
->length
);
51 const char kInitialAuthResponse
[] = "Mary had a little lamb";
53 void EstablishInitialContext(test::MockGSSAPILibrary
* library
) {
54 test::GssContextMockImpl
context_info(
55 "localhost", // Source name
56 "example.com", // Target name
58 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
60 1, // Locally initiated
62 gss_buffer_desc in_buffer
= {0, NULL
};
63 gss_buffer_desc out_buffer
= {arraysize(kInitialAuthResponse
),
64 const_cast<char*>(kInitialAuthResponse
)};
65 library
->ExpectSecurityContext(
67 GSS_S_CONTINUE_NEEDED
,
76 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPIStartup
) {
77 // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
78 // libraries we expect, and also whether or not they have the interface
80 scoped_ptr
<GSSAPILibrary
> gssapi(new GSSAPISharedLibrary(std::string()));
82 EXPECT_TRUE(gssapi
.get()->Init());
85 #if defined(DLOPEN_KERBEROS)
86 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPILoadCustomLibrary
) {
87 scoped_ptr
<GSSAPILibrary
> gssapi(
88 new GSSAPISharedLibrary("/this/library/does/not/exist"));
89 EXPECT_FALSE(gssapi
.get()->Init());
91 #endif // defined(DLOPEN_KERBEROS)
93 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPICycle
) {
94 scoped_ptr
<test::MockGSSAPILibrary
> mock_library(new test::MockGSSAPILibrary
);
95 DCHECK(mock_library
.get());
97 const char kAuthResponse
[] = "Mary had a little lamb";
98 test::GssContextMockImpl
context1(
99 "localhost", // Source name
100 "example.com", // Target name
102 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
104 1, // Locally initiated
106 test::GssContextMockImpl
context2(
107 "localhost", // Source name
108 "example.com", // Target name
110 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
112 1, // Locally initiated
114 test::MockGSSAPILibrary::SecurityContextQuery queries
[] = {
115 test::MockGSSAPILibrary::SecurityContextQuery(
116 "Negotiate", // Package name
117 GSS_S_CONTINUE_NEEDED
, // Major response code
118 0, // Minor response code
120 NULL
, // Expected input token
121 kAuthResponse
), // Output token
122 test::MockGSSAPILibrary::SecurityContextQuery(
123 "Negotiate", // Package name
124 GSS_S_COMPLETE
, // Major response code
125 0, // Minor response code
127 kAuthResponse
, // Expected input token
128 kAuthResponse
) // Output token
131 for (size_t i
= 0; i
< arraysize(queries
); ++i
) {
132 mock_library
->ExpectSecurityContext(queries
[i
].expected_package
,
133 queries
[i
].response_code
,
134 queries
[i
].minor_response_code
,
135 queries
[i
].context_info
,
136 queries
[i
].expected_input_token
,
137 queries
[i
].output_token
);
140 OM_uint32 major_status
= 0;
141 OM_uint32 minor_status
= 0;
142 gss_cred_id_t initiator_cred_handle
= NULL
;
143 gss_ctx_id_t context_handle
= NULL
;
144 gss_name_t target_name
= NULL
;
145 gss_OID mech_type
= NULL
;
146 OM_uint32 req_flags
= 0;
147 OM_uint32 time_req
= 25;
148 gss_channel_bindings_t input_chan_bindings
= NULL
;
149 gss_buffer_desc input_token
= { 0, NULL
};
150 gss_OID actual_mech_type
= NULL
;
151 gss_buffer_desc output_token
= { 0, NULL
};
152 OM_uint32 ret_flags
= 0;
153 OM_uint32 time_rec
= 0;
154 for (size_t i
= 0; i
< arraysize(queries
); ++i
) {
155 major_status
= mock_library
->init_sec_context(&minor_status
,
156 initiator_cred_handle
,
168 EXPECT_EQ(queries
[i
].response_code
, major_status
);
169 CopyBuffer(&input_token
, &output_token
);
170 ClearBuffer(&output_token
);
172 ClearBuffer(&input_token
);
173 major_status
= mock_library
->delete_sec_context(&minor_status
,
176 EXPECT_EQ(static_cast<OM_uint32
>(GSS_S_COMPLETE
), major_status
);
179 TEST(HttpAuthGSSAPITest
, ParseChallenge_FirstRound
) {
180 // The first round should just consist of an unadorned "Negotiate" header.
181 test::MockGSSAPILibrary mock_library
;
182 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
183 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
184 std::string challenge_text
= "Negotiate";
185 HttpAuthChallengeTokenizer
challenge(challenge_text
.begin(),
186 challenge_text
.end());
187 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
188 auth_gssapi
.ParseChallenge(&challenge
));
191 TEST(HttpAuthGSSAPITest
, ParseChallenge_TwoRounds
) {
192 // The first round should just have "Negotiate", and the second round should
193 // have a valid base64 token associated with it.
194 test::MockGSSAPILibrary mock_library
;
195 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
196 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
197 std::string first_challenge_text
= "Negotiate";
198 HttpAuthChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
199 first_challenge_text
.end());
200 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
201 auth_gssapi
.ParseChallenge(&first_challenge
));
203 // Generate an auth token and create another thing.
204 EstablishInitialContext(&mock_library
);
205 std::string auth_token
;
206 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, "HTTP/intranet.google.com",
209 std::string second_challenge_text
= "Negotiate Zm9vYmFy";
210 HttpAuthChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
211 second_challenge_text
.end());
212 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
213 auth_gssapi
.ParseChallenge(&second_challenge
));
216 TEST(HttpAuthGSSAPITest
, ParseChallenge_UnexpectedTokenFirstRound
) {
217 // If the first round challenge has an additional authentication token, it
218 // should be treated as an invalid challenge from the server.
219 test::MockGSSAPILibrary mock_library
;
220 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
221 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
222 std::string challenge_text
= "Negotiate Zm9vYmFy";
223 HttpAuthChallengeTokenizer
challenge(challenge_text
.begin(),
224 challenge_text
.end());
225 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID
,
226 auth_gssapi
.ParseChallenge(&challenge
));
229 TEST(HttpAuthGSSAPITest
, ParseChallenge_MissingTokenSecondRound
) {
230 // If a later-round challenge is simply "Negotiate", it should be treated as
231 // an authentication challenge rejection from the server or proxy.
232 test::MockGSSAPILibrary mock_library
;
233 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
234 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
235 std::string first_challenge_text
= "Negotiate";
236 HttpAuthChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
237 first_challenge_text
.end());
238 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
239 auth_gssapi
.ParseChallenge(&first_challenge
));
241 EstablishInitialContext(&mock_library
);
242 std::string auth_token
;
243 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, "HTTP/intranet.google.com",
245 std::string second_challenge_text
= "Negotiate";
246 HttpAuthChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
247 second_challenge_text
.end());
248 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT
,
249 auth_gssapi
.ParseChallenge(&second_challenge
));
252 TEST(HttpAuthGSSAPITest
, ParseChallenge_NonBase64EncodedToken
) {
253 // If a later-round challenge has an invalid base64 encoded token, it should
254 // be treated as an invalid challenge.
255 test::MockGSSAPILibrary mock_library
;
256 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
257 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
258 std::string first_challenge_text
= "Negotiate";
259 HttpAuthChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
260 first_challenge_text
.end());
261 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
262 auth_gssapi
.ParseChallenge(&first_challenge
));
264 EstablishInitialContext(&mock_library
);
265 std::string auth_token
;
266 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, "HTTP/intranet.google.com",
268 std::string second_challenge_text
= "Negotiate =happyjoy=";
269 HttpAuthChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
270 second_challenge_text
.end());
271 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID
,
272 auth_gssapi
.ParseChallenge(&second_challenge
));