blob: 21eecacf35b07fd95bf7ac4ace3c1d0759f9aa35 [file] [log] [blame]
[email protected]843a6b22012-07-19 17:23:111// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]83727172010-06-04 17:58:082// 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_gssapi_posix.h"
6
danakj1fd259a02016-04-16 03:17:097#include <memory>
8
[email protected]cdefd292010-06-14 12:05:469#include "base/logging.h"
[email protected]83727172010-06-04 17:58:0810#include "base/native_library.h"
[email protected]eca50e122010-09-11 14:03:3011#include "net/base/net_errors.h"
[email protected]df41d0d82014-03-13 00:43:2412#include "net/http/http_auth_challenge_tokenizer.h"
[email protected]3ad259a2010-07-16 17:26:4713#include "net/http/mock_gssapi_library_posix.h"
[email protected]83727172010-06-04 17:58:0814#include "testing/gtest/include/gtest/gtest.h"
15
16namespace net {
17
[email protected]3ad259a2010-07-16 17:26:4718namespace {
19
20// gss_buffer_t helpers.
21void ClearBuffer(gss_buffer_t dest) {
22 if (!dest)
23 return;
24 dest->length = 0;
25 delete [] reinterpret_cast<char*>(dest->value);
26 dest->value = NULL;
27}
28
29void SetBuffer(gss_buffer_t dest, const void* src, size_t length) {
30 if (!dest)
31 return;
32 ClearBuffer(dest);
33 if (!src)
34 return;
35 dest->length = length;
36 if (length) {
37 dest->value = new char[length];
38 memcpy(dest->value, src, length);
39 }
40}
41
42void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) {
43 if (!dest)
44 return;
45 ClearBuffer(dest);
46 if (!src)
47 return;
48 SetBuffer(dest, src->value, src->length);
49}
50
[email protected]eca50e122010-09-11 14:03:3051const char kInitialAuthResponse[] = "Mary had a little lamb";
52
53void EstablishInitialContext(test::MockGSSAPILibrary* library) {
54 test::GssContextMockImpl context_info(
[email protected]3a0c37e2011-07-11 23:14:0355 "localhost", // Source name
56 "example.com", // Target name
57 23, // Lifetime
[email protected]843a6b22012-07-19 17:23:1158 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism
[email protected]3a0c37e2011-07-11 23:14:0359 0, // Context flags
60 1, // Locally initiated
61 0); // Open
[email protected]eca50e122010-09-11 14:03:3062 gss_buffer_desc in_buffer = {0, NULL};
63 gss_buffer_desc out_buffer = {arraysize(kInitialAuthResponse),
64 const_cast<char*>(kInitialAuthResponse)};
65 library->ExpectSecurityContext(
66 "Negotiate",
67 GSS_S_CONTINUE_NEEDED,
68 0,
69 context_info,
70 in_buffer,
71 out_buffer);
72}
73
aberentec894a52015-07-09 14:45:5374void UnexpectedCallback(int result) {
75 // At present getting tokens from gssapi is fully synchronous, so the callback
76 // should never be called.
77 ADD_FAILURE();
78}
79
[email protected]3ad259a2010-07-16 17:26:4780} // namespace
81
[email protected]83727172010-06-04 17:58:0882TEST(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup) {
83 // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
84 // libraries we expect, and also whether or not they have the interface
85 // functions we want.
danakj1fd259a02016-04-16 03:17:0986 std::unique_ptr<GSSAPILibrary> gssapi(new GSSAPISharedLibrary(std::string()));
[email protected]ac7f3fdb2010-11-12 12:47:0587 DCHECK(gssapi.get());
[email protected]0773a482011-05-31 21:37:3788 EXPECT_TRUE(gssapi.get()->Init());
[email protected]ac7f3fdb2010-11-12 12:47:0589}
90
[email protected]6acbd8dd2011-08-24 21:19:2091#if defined(DLOPEN_KERBEROS)
[email protected]ac7f3fdb2010-11-12 12:47:0592TEST(HttpAuthGSSAPIPOSIXTest, GSSAPILoadCustomLibrary) {
danakj1fd259a02016-04-16 03:17:0993 std::unique_ptr<GSSAPILibrary> gssapi(
[email protected]ac7f3fdb2010-11-12 12:47:0594 new GSSAPISharedLibrary("/this/library/does/not/exist"));
[email protected]0773a482011-05-31 21:37:3795 EXPECT_FALSE(gssapi.get()->Init());
[email protected]83727172010-06-04 17:58:0896}
[email protected]6acbd8dd2011-08-24 21:19:2097#endif // defined(DLOPEN_KERBEROS)
[email protected]83727172010-06-04 17:58:0898
[email protected]3ad259a2010-07-16 17:26:4799TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) {
danakj1fd259a02016-04-16 03:17:09100 std::unique_ptr<test::MockGSSAPILibrary> mock_library(
101 new test::MockGSSAPILibrary);
[email protected]3ad259a2010-07-16 17:26:47102 DCHECK(mock_library.get());
103 mock_library->Init();
104 const char kAuthResponse[] = "Mary had a little lamb";
105 test::GssContextMockImpl context1(
[email protected]3a0c37e2011-07-11 23:14:03106 "localhost", // Source name
107 "example.com", // Target name
108 23, // Lifetime
[email protected]843a6b22012-07-19 17:23:11109 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism
[email protected]3a0c37e2011-07-11 23:14:03110 0, // Context flags
111 1, // Locally initiated
112 0); // Open
[email protected]3ad259a2010-07-16 17:26:47113 test::GssContextMockImpl context2(
[email protected]3a0c37e2011-07-11 23:14:03114 "localhost", // Source name
115 "example.com", // Target name
116 23, // Lifetime
[email protected]843a6b22012-07-19 17:23:11117 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism
[email protected]3a0c37e2011-07-11 23:14:03118 0, // Context flags
119 1, // Locally initiated
120 1); // Open
[email protected]3ad259a2010-07-16 17:26:47121 test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
[email protected]5322a7f2011-02-11 20:44:42122 test::MockGSSAPILibrary::SecurityContextQuery(
123 "Negotiate", // Package name
124 GSS_S_CONTINUE_NEEDED, // Major response code
125 0, // Minor response code
126 context1, // Context
127 NULL, // Expected input token
128 kAuthResponse), // Output token
129 test::MockGSSAPILibrary::SecurityContextQuery(
130 "Negotiate", // Package name
131 GSS_S_COMPLETE, // Major response code
132 0, // Minor response code
133 context2, // Context
134 kAuthResponse, // Expected input token
135 kAuthResponse) // Output token
[email protected]3ad259a2010-07-16 17:26:47136 };
137
138 for (size_t i = 0; i < arraysize(queries); ++i) {
139 mock_library->ExpectSecurityContext(queries[i].expected_package,
140 queries[i].response_code,
141 queries[i].minor_response_code,
142 queries[i].context_info,
143 queries[i].expected_input_token,
144 queries[i].output_token);
145 }
146
147 OM_uint32 major_status = 0;
148 OM_uint32 minor_status = 0;
149 gss_cred_id_t initiator_cred_handle = NULL;
150 gss_ctx_id_t context_handle = NULL;
151 gss_name_t target_name = NULL;
152 gss_OID mech_type = NULL;
153 OM_uint32 req_flags = 0;
154 OM_uint32 time_req = 25;
155 gss_channel_bindings_t input_chan_bindings = NULL;
156 gss_buffer_desc input_token = { 0, NULL };
157 gss_OID actual_mech_type= NULL;
158 gss_buffer_desc output_token = { 0, NULL };
159 OM_uint32 ret_flags = 0;
160 OM_uint32 time_rec = 0;
161 for (size_t i = 0; i < arraysize(queries); ++i) {
162 major_status = mock_library->init_sec_context(&minor_status,
163 initiator_cred_handle,
164 &context_handle,
165 target_name,
166 mech_type,
167 req_flags,
168 time_req,
169 input_chan_bindings,
170 &input_token,
171 &actual_mech_type,
172 &output_token,
173 &ret_flags,
174 &time_rec);
[email protected]7c75b4c2011-07-02 14:38:05175 EXPECT_EQ(queries[i].response_code, major_status);
[email protected]3ad259a2010-07-16 17:26:47176 CopyBuffer(&input_token, &output_token);
177 ClearBuffer(&output_token);
178 }
179 ClearBuffer(&input_token);
180 major_status = mock_library->delete_sec_context(&minor_status,
181 &context_handle,
182 GSS_C_NO_BUFFER);
[email protected]7c75b4c2011-07-02 14:38:05183 EXPECT_EQ(static_cast<OM_uint32>(GSS_S_COMPLETE), major_status);
[email protected]3ad259a2010-07-16 17:26:47184}
185
[email protected]eca50e122010-09-11 14:03:30186TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) {
187 // The first round should just consist of an unadorned "Negotiate" header.
188 test::MockGSSAPILibrary mock_library;
189 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
[email protected]843a6b22012-07-19 17:23:11190 CHROME_GSS_SPNEGO_MECH_OID_DESC);
[email protected]eca50e122010-09-11 14:03:30191 std::string challenge_text = "Negotiate";
[email protected]df41d0d82014-03-13 00:43:24192 HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
193 challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30194 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
195 auth_gssapi.ParseChallenge(&challenge));
196}
197
198TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
199 // The first round should just have "Negotiate", and the second round should
200 // have a valid base64 token associated with it.
201 test::MockGSSAPILibrary mock_library;
202 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
[email protected]843a6b22012-07-19 17:23:11203 CHROME_GSS_SPNEGO_MECH_OID_DESC);
[email protected]eca50e122010-09-11 14:03:30204 std::string first_challenge_text = "Negotiate";
[email protected]df41d0d82014-03-13 00:43:24205 HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
206 first_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30207 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
208 auth_gssapi.ParseChallenge(&first_challenge));
209
210 // Generate an auth token and create another thing.
211 EstablishInitialContext(&mock_library);
212 std::string auth_token;
[email protected]bc4e5512013-12-06 07:18:49213 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
asanka5ffd5d72016-03-23 16:20:49214 std::string(), &auth_token,
aberentec894a52015-07-09 14:45:53215 base::Bind(&UnexpectedCallback)));
[email protected]eca50e122010-09-11 14:03:30216
217 std::string second_challenge_text = "Negotiate Zm9vYmFy";
[email protected]df41d0d82014-03-13 00:43:24218 HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
219 second_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30220 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
221 auth_gssapi.ParseChallenge(&second_challenge));
222}
223
224TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) {
225 // If the first round challenge has an additional authentication token, it
226 // should be treated as an invalid challenge from the server.
227 test::MockGSSAPILibrary mock_library;
228 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
[email protected]843a6b22012-07-19 17:23:11229 CHROME_GSS_SPNEGO_MECH_OID_DESC);
[email protected]eca50e122010-09-11 14:03:30230 std::string challenge_text = "Negotiate Zm9vYmFy";
[email protected]df41d0d82014-03-13 00:43:24231 HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
232 challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30233 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
234 auth_gssapi.ParseChallenge(&challenge));
235}
236
237TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
238 // If a later-round challenge is simply "Negotiate", it should be treated as
239 // an authentication challenge rejection from the server or proxy.
240 test::MockGSSAPILibrary mock_library;
241 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
[email protected]843a6b22012-07-19 17:23:11242 CHROME_GSS_SPNEGO_MECH_OID_DESC);
[email protected]eca50e122010-09-11 14:03:30243 std::string first_challenge_text = "Negotiate";
[email protected]df41d0d82014-03-13 00:43:24244 HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
245 first_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30246 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
247 auth_gssapi.ParseChallenge(&first_challenge));
248
249 EstablishInitialContext(&mock_library);
250 std::string auth_token;
[email protected]bc4e5512013-12-06 07:18:49251 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
asanka5ffd5d72016-03-23 16:20:49252 std::string(), &auth_token,
aberentec894a52015-07-09 14:45:53253 base::Bind(&UnexpectedCallback)));
[email protected]eca50e122010-09-11 14:03:30254 std::string second_challenge_text = "Negotiate";
[email protected]df41d0d82014-03-13 00:43:24255 HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
256 second_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30257 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
258 auth_gssapi.ParseChallenge(&second_challenge));
259}
260
261TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
262 // If a later-round challenge has an invalid base64 encoded token, it should
263 // be treated as an invalid challenge.
264 test::MockGSSAPILibrary mock_library;
265 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
[email protected]843a6b22012-07-19 17:23:11266 CHROME_GSS_SPNEGO_MECH_OID_DESC);
[email protected]eca50e122010-09-11 14:03:30267 std::string first_challenge_text = "Negotiate";
[email protected]df41d0d82014-03-13 00:43:24268 HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
269 first_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30270 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
271 auth_gssapi.ParseChallenge(&first_challenge));
272
273 EstablishInitialContext(&mock_library);
274 std::string auth_token;
[email protected]bc4e5512013-12-06 07:18:49275 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
asanka5ffd5d72016-03-23 16:20:49276 std::string(), &auth_token,
aberentec894a52015-07-09 14:45:53277 base::Bind(&UnexpectedCallback)));
[email protected]eca50e122010-09-11 14:03:30278 std::string second_challenge_text = "Negotiate =happyjoy=";
[email protected]df41d0d82014-03-13 00:43:24279 HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
280 second_challenge_text.end());
[email protected]eca50e122010-09-11 14:03:30281 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
282 auth_gssapi.ParseChallenge(&second_challenge));
283}
284
[email protected]83727172010-06-04 17:58:08285} // namespace net