Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Side by Side Diff: net/http/http_auth_gssapi_posix.cc

Issue 2268002: Implemented mid-level Negotiate protocol for Posix. (Closed)
Patch Set: Merged with trunk. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/http/http_auth_gssapi_posix.h ('k') | net/http/http_auth_gssapi_posix_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // 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
7 #include <string>
8
9 #include "base/base64.h"
10 #include "base/file_path.h"
11 #include "base/logging.h"
12 #include "base/native_library.h"
13 #include "base/singleton.h"
14 #include "base/string_util.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_util.h"
17 #include "net/http/http_auth.h"
18
19 namespace net {
20
21 gssapi::gss_OID_desc GSS_C_NT_USER_NAME = {
22 10,
23 (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"
24 };
25 gssapi::gss_OID_desc GSS_C_NT_MACHINE_UID_NAME = {
26 10,
27 (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"
28 };
29 gssapi::gss_OID_desc GSS_C_NT_STRING_UID_NAME = {
30 10,
31 (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"
32 };
33 gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X = {
34 6,
35 (void *) "\x2b\x06\x01\x05\x06\x02"
36 };
37 gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE = {
38 10,
39 (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"
40 };
41 gssapi::gss_OID_desc GSS_C_NT_ANONYMOUS = {
42 6,
43 (void *) "\x2b\x06\01\x05\x06\x03"
44 };
45 gssapi::gss_OID_desc GSS_C_NT_EXPORT_NAME = {
46 6,
47 (void *) "\x2b\x06\x01\x05\x06\x04"
48 };
49
50 GSSAPISharedLibrary::GSSAPISharedLibrary()
51 : initialized_(false),
52 cached_initialize_value_(false),
53 gssapi_library_(NULL),
54 import_name_(NULL),
55 release_name_(NULL),
56 release_buffer_(NULL),
57 display_status_(NULL),
58 init_sec_context_(NULL),
59 wrap_size_limit_(NULL) {
60 }
61
62 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
63 if (gssapi_library_) {
64 base::UnloadNativeLibrary(gssapi_library_);
65 gssapi_library_ = NULL;
66 }
67 }
68
69 bool GSSAPISharedLibrary::Init() {
70 if (!initialized_) {
71 cached_initialize_value_ = InitImpl();
72 initialized_ = true;
73 }
74 return cached_initialize_value_;
75 }
76
77 bool GSSAPISharedLibrary::InitImpl() {
78 DCHECK(!initialized_);
79 gssapi_library_ = LoadSharedObject();
80 if (gssapi_library_ == NULL)
81 return false;
82 if (!BindMethods())
83 return false;
84 return true;
85 }
86
87 const char* GSSAPISharedLibrary::kLibraryNames[] = {
88 "libgssapi_krb5.so.2",
89 "libgssapi.so.4",
90 "libgssapi.so.1",
91 };
92
93 base::NativeLibrary GSSAPISharedLibrary::LoadSharedObject() {
94 for (unsigned int i = 0; i < arraysize(kLibraryNames); ++i) {
95 FilePath file_path(kLibraryNames[i]);
96 base::NativeLibrary lib = base::LoadNativeLibrary(file_path);
97 if (lib)
98 return lib;
99 }
100 return NULL;
101 }
102
103 template <typename T>
104 bool FindAndBind(base::NativeLibrary library, const char* name, T* t) {
105 void* func = base::GetFunctionPointerFromNativeLibrary(library, name);
106 if (func == NULL) {
107 string16 error_message = base::GetLibraryErrorMessage();
108 LOG(WARNING) << "dlsym failed when finding function pointer for " << name
109 << ": " << error_message;
110 return false;
111 }
112 *t = reinterpret_cast<T>(func);
113 return true;
114 }
115
116 bool GSSAPISharedLibrary::BindMethods() {
117 DCHECK(gssapi_library_ != NULL);
118 if (!FindAndBind(gssapi_library_, "gss_import_name", &import_name_))
119 return false;
120 if (!FindAndBind(gssapi_library_, "gss_release_name", &release_name_))
121 return false;
122 if (!FindAndBind(gssapi_library_, "gss_release_buffer", &release_buffer_))
123 return false;
124 if (!FindAndBind(gssapi_library_, "gss_display_status", &display_status_))
125 return false;
126 if (!FindAndBind(gssapi_library_, "gss_init_sec_context", &init_sec_context_))
127 return false;
128 if (!FindAndBind(gssapi_library_, "gss_wrap_size_limit", &wrap_size_limit_))
129 return false;
130 return true;
131 }
132
133 gssapi::OM_uint32 GSSAPISharedLibrary::import_name(
134 gssapi::OM_uint32* minor_status,
135 const gssapi::gss_buffer_t input_name_buffer,
136 const gssapi::gss_OID input_name_type,
137 gssapi::gss_name_t* output_name) {
138 DCHECK(cached_initialize_value_);
139 if (!import_name_)
140 return gssapi::OM_uint32(-1);
141 return import_name_(minor_status, input_name_buffer, input_name_type,
142 output_name);
143 }
144
145 gssapi::OM_uint32 GSSAPISharedLibrary::release_name(
146 gssapi::OM_uint32* minor_status,
147 gssapi::gss_name_t* input_name) {
148 DCHECK(cached_initialize_value_);
149 if (!release_name_)
150 return gssapi::OM_uint32(-1);
151 return release_name_(minor_status, input_name);
152 }
153
154 gssapi::OM_uint32 GSSAPISharedLibrary::release_buffer(
155 gssapi::OM_uint32* minor_status,
156 gssapi::gss_buffer_t buffer) {
157 DCHECK(cached_initialize_value_);
158 if (!release_buffer_)
159 return gssapi::OM_uint32(-1);
160 return release_buffer_(minor_status, buffer);
161 }
162
163 gssapi::OM_uint32 GSSAPISharedLibrary::display_status(
164 gssapi::OM_uint32* minor_status,
165 gssapi::OM_uint32 status_value,
166 int status_type,
167 const gssapi::gss_OID mech_type,
168 gssapi::OM_uint32* message_context,
169 gssapi::gss_buffer_t status_string) {
170 DCHECK(cached_initialize_value_);
171 if (!display_status_)
172 return gssapi::OM_uint32(-1);
173 status_string->length = 0;
174 status_string->value = NULL;
175 return display_status_(minor_status, status_value, status_type, mech_type,
176 message_context, status_string);
177 }
178
179 gssapi::OM_uint32 GSSAPISharedLibrary::init_sec_context(
180 gssapi::OM_uint32* minor_status,
181 const gssapi::gss_cred_id_t initiator_cred_handle,
182 gssapi::gss_ctx_id_t* context_handle,
183 const gssapi::gss_name_t target_name,
184 const gssapi::gss_OID mech_type,
185 gssapi::OM_uint32 req_flags,
186 gssapi::OM_uint32 time_req,
187 const gssapi::gss_channel_bindings_t input_chan_bindings,
188 const gssapi::gss_buffer_t input_token,
189 gssapi::gss_OID* actual_mech_type,
190 gssapi::gss_buffer_t* output_token,
191 gssapi::OM_uint32* ret_flags,
192 gssapi::OM_uint32* time_rec) {
193 DCHECK(cached_initialize_value_);
194 if (!init_sec_context_)
195 return gssapi::OM_uint32(-1);
196 return init_sec_context_(minor_status,
197 initiator_cred_handle,
198 context_handle,
199 target_name,
200 mech_type,
201 req_flags,
202 time_req,
203 input_chan_bindings,
204 input_token,
205 actual_mech_type,
206 reinterpret_cast<gssapi::gss_buffer_t>(output_token),
207 ret_flags,
208 time_rec);
209 }
210
211 gssapi::OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
212 gssapi::OM_uint32* minor_status,
213 const gssapi::gss_ctx_id_t context_handle,
214 int conf_req_flag,
215 gssapi::gss_qop_t qop_req,
216 gssapi::OM_uint32 req_output_size,
217 gssapi::OM_uint32* max_input_size) {
218 DCHECK(cached_initialize_value_);
219 if (!wrap_size_limit_)
220 return gssapi::OM_uint32(-1);
221 return wrap_size_limit_(minor_status,
222 context_handle,
223 conf_req_flag,
224 qop_req,
225 req_output_size,
226 max_input_size);
227 }
228
229 GSSAPILibrary* GSSAPILibrary::GetDefault() {
230 return Singleton<GSSAPISharedLibrary>::get();
231 }
232
233 namespace {
234 std::string DisplayStatus(gssapi::OM_uint32 major_status, gssapi::OM_uint32 mino r_status) {
235 if (major_status == GSS_S_COMPLETE)
236 return "OK";
237 return StringPrintf("%x %x", major_status, minor_status);
238 }
239
240 static const int kMaxDisplayIterations = 8;
241
242 std::string DisplayCode(GSSAPILibrary* gssapi_lib,
243 gssapi::OM_uint32 status,
244 gssapi::OM_uint32 status_code_type) {
245 // msg_ctx needs to be outside the loop because it is invoked multiple times.
246 gssapi::OM_uint32 msg_ctx = 0;
247 std::string rv = StringPrintf("(0x%08X)", status);
248
249 // This loop should continue iterating until msg_ctx is 0 after the first
250 // iteration. To be cautious and prevent an infinite loop, it stops after
251 // a finite number of iterations as well.
252 for (int i = 0; i < kMaxDisplayIterations; ++i) {
253 gssapi::OM_uint32 min_stat;
254 gssapi::gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
255 gssapi_lib->display_status(&min_stat, status, status_code_type,
256 GSS_C_NULL_OID,
257 &msg_ctx, &msg);
258 rv += StringPrintf(" %s", static_cast<char *>(msg.value));
259 gssapi_lib->release_buffer(&min_stat, &msg);
260 if (!msg_ctx)
261 break;
262 }
263 return rv;
264 }
265
266 std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
267 gssapi::OM_uint32 major_status,
268 gssapi::OM_uint32 minor_status) {
269 if (major_status == GSS_S_COMPLETE)
270 return "OK";
271 std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
272 std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
273 return StringPrintf("Major: %s | Minor: %s", major.c_str(), minor.c_str());
274 }
275
276
277 // ScopedName releases a gssapi::gss_name_t when it goes out of scope.
278 class ScopedName {
279 public:
280 ScopedName(gssapi::gss_name_t name,
281 GSSAPILibrary* gssapi_lib)
282 : name_(name),
283 gssapi_lib_(gssapi_lib) {
284 DCHECK(gssapi_lib_);
285 }
286
287 ~ScopedName() {
288 if (name_ != GSS_C_NO_NAME) {
289 gssapi::OM_uint32 minor_status = 0;
290 gssapi::OM_uint32 major_status =
291 gssapi_lib_->release_name(&minor_status, &name_);
292 if (major_status != GSS_S_COMPLETE) {
293 LOG(WARNING) << "Problem releasing name. "
294 << DisplayStatus(major_status, minor_status);
295 }
296 name_ = GSS_C_NO_NAME;
297 }
298 }
299
300 private:
301 gssapi::gss_name_t name_;
302 GSSAPILibrary* gssapi_lib_;
303
304 DISALLOW_COPY_AND_ASSIGN(ScopedName);
305 };
306
307 // ScopedBuffer releases a gssapi::gss_buffer_t when it goes out of scope.
308 class ScopedBuffer {
309 public:
310 ScopedBuffer(gssapi::gss_buffer_t buffer,
311 GSSAPILibrary* gssapi_lib)
312 : buffer_(buffer),
313 gssapi_lib_(gssapi_lib) {
314 DCHECK(gssapi_lib_);
315 }
316
317 ~ScopedBuffer() {
318 if (buffer_ != GSS_C_NO_BUFFER) {
319 gssapi::OM_uint32 minor_status = 0;
320 gssapi::OM_uint32 major_status =
321 gssapi_lib_->release_buffer(&minor_status, buffer_);
322 if (major_status != GSS_S_COMPLETE) {
323 LOG(WARNING) << "Problem releasing buffer. "
324 << DisplayStatus(major_status, minor_status);
325 }
326 buffer_ = GSS_C_NO_BUFFER;
327 }
328 }
329
330 private:
331 gssapi::gss_buffer_t buffer_;
332 GSSAPILibrary* gssapi_lib_;
333
334 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
335 };
336
337 } // namespace
338
339 HttpAuthGSSAPI::HttpAuthGSSAPI(const std::string& scheme,
340 gssapi::gss_OID gss_oid,
341 GSSAPILibrary* library)
342 : scheme_(scheme),
343 gss_oid_(gss_oid),
344 library_(library),
345 sec_context_(NULL) {
346 }
347
348 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
349 }
350
351 bool HttpAuthGSSAPI::NeedsIdentity() const {
352 return decoded_server_auth_token_.empty();
353 }
354
355 bool HttpAuthGSSAPI::IsFinalRound() const {
356 return !NeedsIdentity();
357 }
358
359 bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
360 // Verify the challenge's auth-scheme.
361 if (!tok->valid() ||
362 !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
363 return false;
364
365 tok->set_expect_base64_token(true);
366 if (!tok->GetNext()) {
367 decoded_server_auth_token_.clear();
368 return true;
369 }
370
371 std::string encoded_auth_token = tok->value();
372 std::string decoded_auth_token;
373 bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
374 if (!base64_rv) {
375 LOG(ERROR) << "Base64 decoding of auth token failed.";
376 return false;
377 }
378 decoded_server_auth_token_ = decoded_auth_token;
379 return true;
380 }
381
382
383 static uint8 kHostbasedServiceOidElements[] = {
384 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x01, 0x04};
385 static gssapi::gss_OID_desc kHostbasedServiceOidDesc = {
386 arraysize(kHostbasedServiceOidElements),
387 static_cast<void *>(&kHostbasedServiceOidElements)};
388
389 int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
390 const std::wstring* password,
391 const std::wstring& origin,
392 const HttpRequestInfo* request,
393 const ProxyInfo* proxy,
394 std::string* out_credentials) {
395 DCHECK(library_);
396 DCHECK((username == NULL) == (password == NULL));
397
398 library_->Init();
399
400 if (!IsFinalRound()) {
401 int rv = OnFirstRound(username, password);
402 if (rv != OK)
403 return rv;
404 }
405
406 gssapi::gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
407 input_token.length = decoded_server_auth_token_.length();
408 input_token.value = static_cast<void *>(const_cast<char *>(
409 decoded_server_auth_token_.c_str()));
410 gssapi::gss_buffer_desc output_token1 = GSS_C_EMPTY_BUFFER;
411 gssapi::gss_buffer_t output_token = &output_token1;
412 int rv = GetNextSecurityToken(origin, &input_token, &output_token);
413 if (rv != OK)
414 return rv;
415 ScopedBuffer sb_out_token(output_token, library_);
416
417 // Base64 encode data in output buffer and prepend the scheme.
418 std::string encode_input(static_cast<char*>(output_token->value),
419 output_token->length);
420 std::string encode_output;
421 bool ok = base::Base64Encode(encode_input, &encode_output);
422 if (!ok)
423 return rv;
424 *out_credentials = scheme_ + " " + encode_output;
425 return OK;
426 }
427
428 int HttpAuthGSSAPI::OnFirstRound(const std::wstring* username,
429 const std::wstring* password) {
430 // TODO(cbentzel): Acquire credentials?
431 DCHECK((username == NULL) == (password == NULL));
432 username_ = L"";
433 password_ = L"";
434 if (username) {
435 username_ = *username;
436 password_ = *password;
437 }
438 return OK;
439 }
440
441 int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& origin,
442 gssapi::gss_buffer_t in_token,
443 gssapi::gss_buffer_t* out_token) {
444 // Create a name for the principal
445 // TODO(cbentzel): Should this be username@origin? What about domain?
446 // TODO(cbentzel): Just do this on the first pass?
447 const GURL orig(WideToASCII(origin));
448 std::string origin_principal = GetHostAndPort(orig);
449 gssapi::gss_buffer_desc origin_buffer = GSS_C_EMPTY_BUFFER;
450 origin_buffer.value = static_cast<void *>(const_cast<char *>(
451 origin_principal.c_str()));
452 origin_buffer.length = origin_principal.size() + 1;
453 gssapi::OM_uint32 minor_status_import_name = 0;
454 gssapi::gss_name_t principal_name;
455 gssapi::OM_uint32 major_status_import_name = library_->import_name(
456 &minor_status_import_name,
457 &origin_buffer,
458 &kHostbasedServiceOidDesc,
459 &principal_name);
460 if (major_status_import_name != GSS_S_COMPLETE) {
461 LOG(WARNING) << "Problem importing name. "
462 << DisplayExtendedStatus(library_,
463 major_status_import_name,
464 minor_status_import_name);
465 return ERR_UNEXPECTED;
466 }
467 ScopedName scoped_name(principal_name, library_);
468
469 // Create a security context.
470 gssapi::OM_uint32 minor_status_sec_context;
471 gssapi::OM_uint32 req_flags = 0;
472 gssapi::OM_uint32 major_status_sec_context = library_->init_sec_context(
473 &minor_status_sec_context,
474 GSS_C_NO_CREDENTIAL,
475 &sec_context_,
476 principal_name,
477 gss_oid_,
478 req_flags,
479 GSS_C_INDEFINITE,
480 GSS_C_NO_CHANNEL_BINDINGS,
481 in_token,
482 NULL, // actual_mech_type
483 out_token,
484 NULL, // ret flags
485 NULL);
486 if (major_status_sec_context != GSS_S_COMPLETE &&
487 major_status_sec_context != GSS_S_CONTINUE_NEEDED) {
488 LOG(WARNING) << "Problem initializing context. "
489 << DisplayExtendedStatus(library_,
490 major_status_sec_context,
491 minor_status_sec_context);
492 return ERR_UNEXPECTED;
493 }
494
495 return (major_status_sec_context != GSS_S_COMPLETE) ? ERR_IO_PENDING : OK;
496 }
497
498 } // namespace net
499
OLDNEW
« no previous file with comments | « net/http/http_auth_gssapi_posix.h ('k') | net/http/http_auth_gssapi_posix_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698