• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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