blob: 62bae711ca9ff375d7df5bc2b4f9910e94ec73b5 [file] [log] [blame]
[email protected]83727172010-06-04 17:58:081// Copyright (c) 2010 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_gssapi_posix.h"
6
[email protected]3ad259a2010-07-16 17:26:477#include "base/basictypes.h"
[email protected]cdefd292010-06-14 12:05:468#include "base/logging.h"
[email protected]83727172010-06-04 17:58:089#include "base/native_library.h"
[email protected]3ad259a2010-07-16 17:26:4710#include "base/scoped_ptr.h"
[email protected]eca50e122010-09-11 14:03:3011#include "net/base/net_errors.h"
[email protected]3ad259a2010-07-16 17:26:4712#include "net/http/mock_gssapi_library_posix.h"
[email protected]83727172010-06-04 17:58:0813#include "testing/gtest/include/gtest/gtest.h"
14
15namespace net {
16
[email protected]3ad259a2010-07-16 17:26:4717namespace {
18
19// gss_buffer_t helpers.
20void ClearBuffer(gss_buffer_t dest) {
21 if (!dest)
22 return;
23 dest->length = 0;
24 delete [] reinterpret_cast<char*>(dest->value);
25 dest->value = NULL;
26}
27
28void SetBuffer(gss_buffer_t dest, const void* src, size_t length) {
29 if (!dest)
30 return;
31 ClearBuffer(dest);
32 if (!src)
33 return;
34 dest->length = length;
35 if (length) {
36 dest->value = new char[length];
37 memcpy(dest->value, src, length);
38 }
39}
40
41void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) {
42 if (!dest)
43 return;
44 ClearBuffer(dest);
45 if (!src)
46 return;
47 SetBuffer(dest, src->value, src->length);
48}
49
[email protected]eca50e122010-09-11 14:03:3050const char kInitialAuthResponse[] = "Mary had a little lamb";
51
52void EstablishInitialContext(test::MockGSSAPILibrary* library) {
53 test::GssContextMockImpl context_info(
54 "localhost", // Source name
55 "example.com", // Target name
56 23, // Lifetime
57 *GSS_C_NT_HOSTBASED_SERVICE, // Mechanism
58 0, // Context flags
59 1, // Locally initiated
60 0); // Open
61 gss_buffer_desc in_buffer = {0, NULL};
62 gss_buffer_desc out_buffer = {arraysize(kInitialAuthResponse),
63 const_cast<char*>(kInitialAuthResponse)};
64 library->ExpectSecurityContext(
65 "Negotiate",
66 GSS_S_CONTINUE_NEEDED,
67 0,
68 context_info,
69 in_buffer,
70 out_buffer);
71}
72
[email protected]3ad259a2010-07-16 17:26:4773} // namespace
74
[email protected]83727172010-06-04 17:58:0875TEST(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
78 // functions we want.
[email protected]83727172010-06-04 17:58:0879 GSSAPILibrary* gssapi = GSSAPILibrary::GetDefault();
[email protected]cdefd292010-06-14 12:05:4680 DCHECK(gssapi);
81 gssapi->Init();
[email protected]83727172010-06-04 17:58:0882}
83
[email protected]3ad259a2010-07-16 17:26:4784TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) {
85 scoped_ptr<test::MockGSSAPILibrary> mock_library(new test::MockGSSAPILibrary);
86 DCHECK(mock_library.get());
87 mock_library->Init();
88 const char kAuthResponse[] = "Mary had a little lamb";
89 test::GssContextMockImpl context1(
90 "localhost", // Source name
91 "example.com", // Target name
92 23, // Lifetime
93 *GSS_C_NT_HOSTBASED_SERVICE, // Mechanism
94 0, // Context flags
95 1, // Locally initiated
96 0); // Open
97 test::GssContextMockImpl context2(
98 "localhost", // Source name
99 "example.com", // Target name
100 23, // Lifetime
101 *GSS_C_NT_HOSTBASED_SERVICE, // Mechanism
102 0, // Context flags
103 1, // Locally initiated
104 1); // Open
105 test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
106 { "Negotiate", // Package name
107 GSS_S_CONTINUE_NEEDED, // Major response code
108 0, // Minor response code
109 context1, // Context
110 { 0, NULL }, // Expected input token
111 { arraysize(kAuthResponse),
112 const_cast<char*>(kAuthResponse) } // Output token
113 },
114 { "Negotiate", // Package name
115 GSS_S_COMPLETE, // Major response code
116 0, // Minor response code
117 context2, // Context
118 { arraysize(kAuthResponse),
119 const_cast<char*>(kAuthResponse) }, // Expected input token
120 { arraysize(kAuthResponse),
121 const_cast<char*>(kAuthResponse) } // Output token
122 },
123 };
124
125 for (size_t i = 0; i < arraysize(queries); ++i) {
126 mock_library->ExpectSecurityContext(queries[i].expected_package,
127 queries[i].response_code,
128 queries[i].minor_response_code,
129 queries[i].context_info,
130 queries[i].expected_input_token,
131 queries[i].output_token);
132 }
133
134 OM_uint32 major_status = 0;
135 OM_uint32 minor_status = 0;
136 gss_cred_id_t initiator_cred_handle = NULL;
137 gss_ctx_id_t context_handle = NULL;
138 gss_name_t target_name = NULL;
139 gss_OID mech_type = NULL;
140 OM_uint32 req_flags = 0;
141 OM_uint32 time_req = 25;
142 gss_channel_bindings_t input_chan_bindings = NULL;
143 gss_buffer_desc input_token = { 0, NULL };
144 gss_OID actual_mech_type= NULL;
145 gss_buffer_desc output_token = { 0, NULL };
146 OM_uint32 ret_flags = 0;
147 OM_uint32 time_rec = 0;
148 for (size_t i = 0; i < arraysize(queries); ++i) {
149 major_status = mock_library->init_sec_context(&minor_status,
150 initiator_cred_handle,
151 &context_handle,
152 target_name,
153 mech_type,
154 req_flags,
155 time_req,
156 input_chan_bindings,
157 &input_token,
158 &actual_mech_type,
159 &output_token,
160 &ret_flags,
161 &time_rec);
162 CopyBuffer(&input_token, &output_token);
163 ClearBuffer(&output_token);
164 }
165 ClearBuffer(&input_token);
166 major_status = mock_library->delete_sec_context(&minor_status,
167 &context_handle,
168 GSS_C_NO_BUFFER);
169}
170
[email protected]eca50e122010-09-11 14:03:30171TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) {
172 // The first round should just consist of an unadorned "Negotiate" header.
173 test::MockGSSAPILibrary mock_library;
174 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
175 CHROME_GSS_KRB5_MECH_OID_DESC);
176 std::string challenge_text = "Negotiate";
177 HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
178 challenge_text.end());
179 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
180 auth_gssapi.ParseChallenge(&challenge));
181}
182
183TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
184 // The first round should just have "Negotiate", and the second round should
185 // have a valid base64 token associated with it.
186 test::MockGSSAPILibrary mock_library;
187 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
188 CHROME_GSS_KRB5_MECH_OID_DESC);
189 std::string first_challenge_text = "Negotiate";
190 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
191 first_challenge_text.end());
192 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
193 auth_gssapi.ParseChallenge(&first_challenge));
194
195 // Generate an auth token and create another thing.
196 EstablishInitialContext(&mock_library);
197 std::string auth_token;
198 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
199 L"HTTP/intranet.google.com",
200 &auth_token));
201
202 std::string second_challenge_text = "Negotiate Zm9vYmFy";
203 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
204 second_challenge_text.end());
205 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
206 auth_gssapi.ParseChallenge(&second_challenge));
207}
208
209TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) {
210 // If the first round challenge has an additional authentication token, it
211 // should be treated as an invalid challenge from the server.
212 test::MockGSSAPILibrary mock_library;
213 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
214 CHROME_GSS_KRB5_MECH_OID_DESC);
215 std::string challenge_text = "Negotiate Zm9vYmFy";
216 HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
217 challenge_text.end());
218 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
219 auth_gssapi.ParseChallenge(&challenge));
220}
221
222TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
223 // If a later-round challenge is simply "Negotiate", it should be treated as
224 // an authentication challenge rejection from the server or proxy.
225 test::MockGSSAPILibrary mock_library;
226 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
227 CHROME_GSS_KRB5_MECH_OID_DESC);
228 std::string first_challenge_text = "Negotiate";
229 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
230 first_challenge_text.end());
231 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
232 auth_gssapi.ParseChallenge(&first_challenge));
233
234 EstablishInitialContext(&mock_library);
235 std::string auth_token;
236 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
237 L"HTTP/intranet.google.com",
238 &auth_token));
239 std::string second_challenge_text = "Negotiate";
240 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
241 second_challenge_text.end());
242 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
243 auth_gssapi.ParseChallenge(&second_challenge));
244}
245
246TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
247 // If a later-round challenge has an invalid base64 encoded token, it should
248 // be treated as an invalid challenge.
249 test::MockGSSAPILibrary mock_library;
250 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
251 CHROME_GSS_KRB5_MECH_OID_DESC);
252 std::string first_challenge_text = "Negotiate";
253 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
254 first_challenge_text.end());
255 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
256 auth_gssapi.ParseChallenge(&first_challenge));
257
258 EstablishInitialContext(&mock_library);
259 std::string auth_token;
260 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
261 L"HTTP/intranet.google.com",
262 &auth_token));
263 std::string second_challenge_text = "Negotiate =happyjoy=";
264 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
265 second_challenge_text.end());
266 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
267 auth_gssapi.ParseChallenge(&second_challenge));
268}
269
[email protected]83727172010-06-04 17:58:08270} // namespace net