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/mock_gssapi_library_posix.h"
13 #include "testing/gtest/include/gtest/gtest.h"
19 // gss_buffer_t helpers.
20 void ClearBuffer(gss_buffer_t dest
) {
24 delete [] reinterpret_cast<char*>(dest
->value
);
28 void SetBuffer(gss_buffer_t dest
, const void* src
, size_t length
) {
34 dest
->length
= length
;
36 dest
->value
= new char[length
];
37 memcpy(dest
->value
, src
, length
);
41 void CopyBuffer(gss_buffer_t dest
, const gss_buffer_t src
) {
47 SetBuffer(dest
, src
->value
, src
->length
);
50 const char kInitialAuthResponse
[] = "Mary had a little lamb";
52 void EstablishInitialContext(test::MockGSSAPILibrary
* library
) {
53 test::GssContextMockImpl
context_info(
54 "localhost", // Source name
55 "example.com", // Target name
57 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
59 1, // Locally initiated
61 gss_buffer_desc in_buffer
= {0, NULL
};
62 gss_buffer_desc out_buffer
= {arraysize(kInitialAuthResponse
),
63 const_cast<char*>(kInitialAuthResponse
)};
64 library
->ExpectSecurityContext(
66 GSS_S_CONTINUE_NEEDED
,
75 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPIStartup
) {
76 // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
77 // libraries we expect, and also whether or not they have the interface
79 scoped_ptr
<GSSAPILibrary
> gssapi(new GSSAPISharedLibrary(std::string()));
81 EXPECT_TRUE(gssapi
.get()->Init());
84 #if defined(DLOPEN_KERBEROS)
85 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPILoadCustomLibrary
) {
86 scoped_ptr
<GSSAPILibrary
> gssapi(
87 new GSSAPISharedLibrary("/this/library/does/not/exist"));
88 EXPECT_FALSE(gssapi
.get()->Init());
90 #endif // defined(DLOPEN_KERBEROS)
92 TEST(HttpAuthGSSAPIPOSIXTest
, GSSAPICycle
) {
93 scoped_ptr
<test::MockGSSAPILibrary
> mock_library(new test::MockGSSAPILibrary
);
94 DCHECK(mock_library
.get());
96 const char kAuthResponse
[] = "Mary had a little lamb";
97 test::GssContextMockImpl
context1(
98 "localhost", // Source name
99 "example.com", // Target name
101 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
103 1, // Locally initiated
105 test::GssContextMockImpl
context2(
106 "localhost", // Source name
107 "example.com", // Target name
109 *CHROME_GSS_SPNEGO_MECH_OID_DESC
, // Mechanism
111 1, // Locally initiated
113 test::MockGSSAPILibrary::SecurityContextQuery queries
[] = {
114 test::MockGSSAPILibrary::SecurityContextQuery(
115 "Negotiate", // Package name
116 GSS_S_CONTINUE_NEEDED
, // Major response code
117 0, // Minor response code
119 NULL
, // Expected input token
120 kAuthResponse
), // Output token
121 test::MockGSSAPILibrary::SecurityContextQuery(
122 "Negotiate", // Package name
123 GSS_S_COMPLETE
, // Major response code
124 0, // Minor response code
126 kAuthResponse
, // Expected input token
127 kAuthResponse
) // Output token
130 for (size_t i
= 0; i
< arraysize(queries
); ++i
) {
131 mock_library
->ExpectSecurityContext(queries
[i
].expected_package
,
132 queries
[i
].response_code
,
133 queries
[i
].minor_response_code
,
134 queries
[i
].context_info
,
135 queries
[i
].expected_input_token
,
136 queries
[i
].output_token
);
139 OM_uint32 major_status
= 0;
140 OM_uint32 minor_status
= 0;
141 gss_cred_id_t initiator_cred_handle
= NULL
;
142 gss_ctx_id_t context_handle
= NULL
;
143 gss_name_t target_name
= NULL
;
144 gss_OID mech_type
= NULL
;
145 OM_uint32 req_flags
= 0;
146 OM_uint32 time_req
= 25;
147 gss_channel_bindings_t input_chan_bindings
= NULL
;
148 gss_buffer_desc input_token
= { 0, NULL
};
149 gss_OID actual_mech_type
= NULL
;
150 gss_buffer_desc output_token
= { 0, NULL
};
151 OM_uint32 ret_flags
= 0;
152 OM_uint32 time_rec
= 0;
153 for (size_t i
= 0; i
< arraysize(queries
); ++i
) {
154 major_status
= mock_library
->init_sec_context(&minor_status
,
155 initiator_cred_handle
,
167 EXPECT_EQ(queries
[i
].response_code
, major_status
);
168 CopyBuffer(&input_token
, &output_token
);
169 ClearBuffer(&output_token
);
171 ClearBuffer(&input_token
);
172 major_status
= mock_library
->delete_sec_context(&minor_status
,
175 EXPECT_EQ(static_cast<OM_uint32
>(GSS_S_COMPLETE
), major_status
);
178 TEST(HttpAuthGSSAPITest
, ParseChallenge_FirstRound
) {
179 // The first round should just consist of an unadorned "Negotiate" header.
180 test::MockGSSAPILibrary mock_library
;
181 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
182 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
183 std::string challenge_text
= "Negotiate";
184 HttpAuth::ChallengeTokenizer
challenge(challenge_text
.begin(),
185 challenge_text
.end());
186 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
187 auth_gssapi
.ParseChallenge(&challenge
));
190 TEST(HttpAuthGSSAPITest
, ParseChallenge_TwoRounds
) {
191 // The first round should just have "Negotiate", and the second round should
192 // have a valid base64 token associated with it.
193 test::MockGSSAPILibrary mock_library
;
194 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
195 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
196 std::string first_challenge_text
= "Negotiate";
197 HttpAuth::ChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
198 first_challenge_text
.end());
199 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
200 auth_gssapi
.ParseChallenge(&first_challenge
));
202 // Generate an auth token and create another thing.
203 EstablishInitialContext(&mock_library
);
204 std::string auth_token
;
205 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, L
"HTTP/intranet.google.com",
208 std::string second_challenge_text
= "Negotiate Zm9vYmFy";
209 HttpAuth::ChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
210 second_challenge_text
.end());
211 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
212 auth_gssapi
.ParseChallenge(&second_challenge
));
215 TEST(HttpAuthGSSAPITest
, ParseChallenge_UnexpectedTokenFirstRound
) {
216 // If the first round challenge has an additional authentication token, it
217 // should be treated as an invalid challenge from the server.
218 test::MockGSSAPILibrary mock_library
;
219 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
220 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
221 std::string challenge_text
= "Negotiate Zm9vYmFy";
222 HttpAuth::ChallengeTokenizer
challenge(challenge_text
.begin(),
223 challenge_text
.end());
224 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID
,
225 auth_gssapi
.ParseChallenge(&challenge
));
228 TEST(HttpAuthGSSAPITest
, ParseChallenge_MissingTokenSecondRound
) {
229 // If a later-round challenge is simply "Negotiate", it should be treated as
230 // an authentication challenge rejection from the server or proxy.
231 test::MockGSSAPILibrary mock_library
;
232 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
233 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
234 std::string first_challenge_text
= "Negotiate";
235 HttpAuth::ChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
236 first_challenge_text
.end());
237 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
238 auth_gssapi
.ParseChallenge(&first_challenge
));
240 EstablishInitialContext(&mock_library
);
241 std::string auth_token
;
242 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, L
"HTTP/intranet.google.com",
244 std::string second_challenge_text
= "Negotiate";
245 HttpAuth::ChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
246 second_challenge_text
.end());
247 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT
,
248 auth_gssapi
.ParseChallenge(&second_challenge
));
251 TEST(HttpAuthGSSAPITest
, ParseChallenge_NonBase64EncodedToken
) {
252 // If a later-round challenge has an invalid base64 encoded token, it should
253 // be treated as an invalid challenge.
254 test::MockGSSAPILibrary mock_library
;
255 HttpAuthGSSAPI
auth_gssapi(&mock_library
, "Negotiate",
256 CHROME_GSS_SPNEGO_MECH_OID_DESC
);
257 std::string first_challenge_text
= "Negotiate";
258 HttpAuth::ChallengeTokenizer
first_challenge(first_challenge_text
.begin(),
259 first_challenge_text
.end());
260 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT
,
261 auth_gssapi
.ParseChallenge(&first_challenge
));
263 EstablishInitialContext(&mock_library
);
264 std::string auth_token
;
265 EXPECT_EQ(OK
, auth_gssapi
.GenerateAuthToken(NULL
, L
"HTTP/intranet.google.com",
267 std::string second_challenge_text
= "Negotiate =happyjoy=";
268 HttpAuth::ChallengeTokenizer
second_challenge(second_challenge_text
.begin(),
269 second_challenge_text
.end());
270 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID
,
271 auth_gssapi
.ParseChallenge(&second_challenge
));