1 // Copyright (c) 2011 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
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"
14
15 namespace net {
16
17 namespace {
18
19 // gss_buffer_t helpers.
ClearBuffer(gss_buffer_t dest)20 void 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
SetBuffer(gss_buffer_t dest,const void * src,size_t length)28 void 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
CopyBuffer(gss_buffer_t dest,const gss_buffer_t src)41 void 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
50 const char kInitialAuthResponse[] = "Mary had a little lamb";
51
EstablishInitialContext(test::MockGSSAPILibrary * library)52 void 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
73 } // namespace
74
TEST(HttpAuthGSSAPIPOSIXTest,GSSAPIStartup)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
78 // functions we want.
79 scoped_ptr<GSSAPILibrary> gssapi(new GSSAPISharedLibrary(""));
80 DCHECK(gssapi.get());
81 DCHECK(gssapi.get()->Init());
82 }
83
TEST(HttpAuthGSSAPIPOSIXTest,GSSAPILoadCustomLibrary)84 TEST(HttpAuthGSSAPIPOSIXTest, GSSAPILoadCustomLibrary) {
85 scoped_ptr<GSSAPILibrary> gssapi(
86 new GSSAPISharedLibrary("/this/library/does/not/exist"));
87 DCHECK(!gssapi.get()->Init());
88 }
89
TEST(HttpAuthGSSAPIPOSIXTest,GSSAPICycle)90 TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) {
91 scoped_ptr<test::MockGSSAPILibrary> mock_library(new test::MockGSSAPILibrary);
92 DCHECK(mock_library.get());
93 mock_library->Init();
94 const char kAuthResponse[] = "Mary had a little lamb";
95 test::GssContextMockImpl context1(
96 "localhost", // Source name
97 "example.com", // Target name
98 23, // Lifetime
99 *GSS_C_NT_HOSTBASED_SERVICE, // Mechanism
100 0, // Context flags
101 1, // Locally initiated
102 0); // Open
103 test::GssContextMockImpl context2(
104 "localhost", // Source name
105 "example.com", // Target name
106 23, // Lifetime
107 *GSS_C_NT_HOSTBASED_SERVICE, // Mechanism
108 0, // Context flags
109 1, // Locally initiated
110 1); // Open
111 test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
112 test::MockGSSAPILibrary::SecurityContextQuery(
113 "Negotiate", // Package name
114 GSS_S_CONTINUE_NEEDED, // Major response code
115 0, // Minor response code
116 context1, // Context
117 NULL, // Expected input token
118 kAuthResponse), // Output token
119 test::MockGSSAPILibrary::SecurityContextQuery(
120 "Negotiate", // Package name
121 GSS_S_COMPLETE, // Major response code
122 0, // Minor response code
123 context2, // Context
124 kAuthResponse, // Expected input token
125 kAuthResponse) // Output token
126 };
127
128 for (size_t i = 0; i < arraysize(queries); ++i) {
129 mock_library->ExpectSecurityContext(queries[i].expected_package,
130 queries[i].response_code,
131 queries[i].minor_response_code,
132 queries[i].context_info,
133 queries[i].expected_input_token,
134 queries[i].output_token);
135 }
136
137 OM_uint32 major_status = 0;
138 OM_uint32 minor_status = 0;
139 gss_cred_id_t initiator_cred_handle = NULL;
140 gss_ctx_id_t context_handle = NULL;
141 gss_name_t target_name = NULL;
142 gss_OID mech_type = NULL;
143 OM_uint32 req_flags = 0;
144 OM_uint32 time_req = 25;
145 gss_channel_bindings_t input_chan_bindings = NULL;
146 gss_buffer_desc input_token = { 0, NULL };
147 gss_OID actual_mech_type= NULL;
148 gss_buffer_desc output_token = { 0, NULL };
149 OM_uint32 ret_flags = 0;
150 OM_uint32 time_rec = 0;
151 for (size_t i = 0; i < arraysize(queries); ++i) {
152 major_status = mock_library->init_sec_context(&minor_status,
153 initiator_cred_handle,
154 &context_handle,
155 target_name,
156 mech_type,
157 req_flags,
158 time_req,
159 input_chan_bindings,
160 &input_token,
161 &actual_mech_type,
162 &output_token,
163 &ret_flags,
164 &time_rec);
165 CopyBuffer(&input_token, &output_token);
166 ClearBuffer(&output_token);
167 }
168 ClearBuffer(&input_token);
169 major_status = mock_library->delete_sec_context(&minor_status,
170 &context_handle,
171 GSS_C_NO_BUFFER);
172 }
173
TEST(HttpAuthGSSAPITest,ParseChallenge_FirstRound)174 TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) {
175 // The first round should just consist of an unadorned "Negotiate" header.
176 test::MockGSSAPILibrary mock_library;
177 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
178 CHROME_GSS_KRB5_MECH_OID_DESC);
179 std::string challenge_text = "Negotiate";
180 HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
181 challenge_text.end());
182 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
183 auth_gssapi.ParseChallenge(&challenge));
184 }
185
TEST(HttpAuthGSSAPITest,ParseChallenge_TwoRounds)186 TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
187 // The first round should just have "Negotiate", and the second round should
188 // have a valid base64 token associated with it.
189 test::MockGSSAPILibrary mock_library;
190 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
191 CHROME_GSS_KRB5_MECH_OID_DESC);
192 std::string first_challenge_text = "Negotiate";
193 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
194 first_challenge_text.end());
195 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
196 auth_gssapi.ParseChallenge(&first_challenge));
197
198 // Generate an auth token and create another thing.
199 EstablishInitialContext(&mock_library);
200 std::string auth_token;
201 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
202 L"HTTP/intranet.google.com",
203 &auth_token));
204
205 std::string second_challenge_text = "Negotiate Zm9vYmFy";
206 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
207 second_challenge_text.end());
208 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
209 auth_gssapi.ParseChallenge(&second_challenge));
210 }
211
TEST(HttpAuthGSSAPITest,ParseChallenge_UnexpectedTokenFirstRound)212 TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) {
213 // If the first round challenge has an additional authentication token, it
214 // should be treated as an invalid challenge from the server.
215 test::MockGSSAPILibrary mock_library;
216 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
217 CHROME_GSS_KRB5_MECH_OID_DESC);
218 std::string challenge_text = "Negotiate Zm9vYmFy";
219 HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
220 challenge_text.end());
221 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
222 auth_gssapi.ParseChallenge(&challenge));
223 }
224
TEST(HttpAuthGSSAPITest,ParseChallenge_MissingTokenSecondRound)225 TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
226 // If a later-round challenge is simply "Negotiate", it should be treated as
227 // an authentication challenge rejection from the server or proxy.
228 test::MockGSSAPILibrary mock_library;
229 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
230 CHROME_GSS_KRB5_MECH_OID_DESC);
231 std::string first_challenge_text = "Negotiate";
232 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
233 first_challenge_text.end());
234 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
235 auth_gssapi.ParseChallenge(&first_challenge));
236
237 EstablishInitialContext(&mock_library);
238 std::string auth_token;
239 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
240 L"HTTP/intranet.google.com",
241 &auth_token));
242 std::string second_challenge_text = "Negotiate";
243 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
244 second_challenge_text.end());
245 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
246 auth_gssapi.ParseChallenge(&second_challenge));
247 }
248
TEST(HttpAuthGSSAPITest,ParseChallenge_NonBase64EncodedToken)249 TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
250 // If a later-round challenge has an invalid base64 encoded token, it should
251 // be treated as an invalid challenge.
252 test::MockGSSAPILibrary mock_library;
253 HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
254 CHROME_GSS_KRB5_MECH_OID_DESC);
255 std::string first_challenge_text = "Negotiate";
256 HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
257 first_challenge_text.end());
258 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
259 auth_gssapi.ParseChallenge(&first_challenge));
260
261 EstablishInitialContext(&mock_library);
262 std::string auth_token;
263 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, NULL,
264 L"HTTP/intranet.google.com",
265 &auth_token));
266 std::string second_challenge_text = "Negotiate =happyjoy=";
267 HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
268 second_challenge_text.end());
269 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
270 auth_gssapi.ParseChallenge(&second_challenge));
271 }
272
273 } // namespace net
274