OLD | NEW |
| (Empty) |
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. | |
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/http_auth_challenge_tokenizer.h" | |
13 #include "net/http/mock_gssapi_library_posix.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 namespace net { | |
17 | |
18 namespace { | |
19 | |
20 // gss_buffer_t helpers. | |
21 void 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 | |
29 void 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 | |
42 void 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 | |
51 const char kInitialAuthResponse[] = "Mary had a little lamb"; | |
52 | |
53 void EstablishInitialContext(test::MockGSSAPILibrary* library) { | |
54 test::GssContextMockImpl context_info( | |
55 "localhost", // Source name | |
56 "example.com", // Target name | |
57 23, // Lifetime | |
58 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism | |
59 0, // Context flags | |
60 1, // Locally initiated | |
61 0); // Open | |
62 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 | |
74 } // namespace | |
75 | |
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 | |
79 // functions we want. | |
80 scoped_ptr<GSSAPILibrary> gssapi(new GSSAPISharedLibrary(std::string())); | |
81 DCHECK(gssapi.get()); | |
82 EXPECT_TRUE(gssapi.get()->Init()); | |
83 } | |
84 | |
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()); | |
90 } | |
91 #endif // defined(DLOPEN_KERBEROS) | |
92 | |
93 TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) { | |
94 scoped_ptr<test::MockGSSAPILibrary> mock_library(new test::MockGSSAPILibrary); | |
95 DCHECK(mock_library.get()); | |
96 mock_library->Init(); | |
97 const char kAuthResponse[] = "Mary had a little lamb"; | |
98 test::GssContextMockImpl context1( | |
99 "localhost", // Source name | |
100 "example.com", // Target name | |
101 23, // Lifetime | |
102 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism | |
103 0, // Context flags | |
104 1, // Locally initiated | |
105 0); // Open | |
106 test::GssContextMockImpl context2( | |
107 "localhost", // Source name | |
108 "example.com", // Target name | |
109 23, // Lifetime | |
110 *CHROME_GSS_SPNEGO_MECH_OID_DESC, // Mechanism | |
111 0, // Context flags | |
112 1, // Locally initiated | |
113 1); // Open | |
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 | |
119 context1, // Context | |
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 | |
126 context2, // Context | |
127 kAuthResponse, // Expected input token | |
128 kAuthResponse) // Output token | |
129 }; | |
130 | |
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); | |
138 } | |
139 | |
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, | |
157 &context_handle, | |
158 target_name, | |
159 mech_type, | |
160 req_flags, | |
161 time_req, | |
162 input_chan_bindings, | |
163 &input_token, | |
164 &actual_mech_type, | |
165 &output_token, | |
166 &ret_flags, | |
167 &time_rec); | |
168 EXPECT_EQ(queries[i].response_code, major_status); | |
169 CopyBuffer(&input_token, &output_token); | |
170 ClearBuffer(&output_token); | |
171 } | |
172 ClearBuffer(&input_token); | |
173 major_status = mock_library->delete_sec_context(&minor_status, | |
174 &context_handle, | |
175 GSS_C_NO_BUFFER); | |
176 EXPECT_EQ(static_cast<OM_uint32>(GSS_S_COMPLETE), major_status); | |
177 } | |
178 | |
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)); | |
189 } | |
190 | |
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)); | |
202 | |
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", | |
207 &auth_token)); | |
208 | |
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)); | |
214 } | |
215 | |
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)); | |
227 } | |
228 | |
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)); | |
240 | |
241 EstablishInitialContext(&mock_library); | |
242 std::string auth_token; | |
243 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", | |
244 &auth_token)); | |
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)); | |
250 } | |
251 | |
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)); | |
263 | |
264 EstablishInitialContext(&mock_library); | |
265 std::string auth_token; | |
266 EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", | |
267 &auth_token)); | |
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)); | |
273 } | |
274 | |
275 } // namespace net | |
OLD | NEW |