Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / net / http / http_auth_gssapi_posix.cc
blob9b902af1728e36d427802ddb503f3e61c336ab3b
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.
5 #include "net/http/http_auth_gssapi_posix.h"
7 #include <limits>
8 #include <string>
10 #include "base/base64.h"
11 #include "base/files/file_path.h"
12 #include "base/format_macros.h"
13 #include "base/logging.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/net_util.h"
19 #include "net/http/http_auth_challenge_tokenizer.h"
21 // These are defined for the GSSAPI library:
22 // Paraphrasing the comments from gssapi.h:
23 // "The implementation must reserve static storage for a
24 // gss_OID_desc object for each constant. That constant
25 // should be initialized to point to that gss_OID_desc."
26 // These are encoded using ASN.1 BER encoding.
27 namespace {
29 static gss_OID_desc GSS_C_NT_USER_NAME_VAL = {
30 10,
31 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
33 static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL = {
34 10,
35 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
37 static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL = {
38 10,
39 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
41 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
43 const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
45 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL = {
46 10,
47 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
49 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = {
51 const_cast<char*>("\x2b\x06\01\x05\x06\x03")
53 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = {
55 const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
58 } // namespace
60 // Heimdal >= 1.4 will define the following as preprocessor macros.
61 // To avoid conflicting declarations, we have to undefine these.
62 #undef GSS_C_NT_USER_NAME
63 #undef GSS_C_NT_MACHINE_UID_NAME
64 #undef GSS_C_NT_STRING_UID_NAME
65 #undef GSS_C_NT_HOSTBASED_SERVICE_X
66 #undef GSS_C_NT_HOSTBASED_SERVICE
67 #undef GSS_C_NT_ANONYMOUS
68 #undef GSS_C_NT_EXPORT_NAME
70 gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_VAL;
71 gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_VAL;
72 gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_VAL;
73 gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL;
74 gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL;
75 gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_VAL;
76 gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_VAL;
78 namespace net {
80 // Exported mechanism for GSSAPI. We always use SPNEGO:
82 // iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
83 gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = {
85 const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
88 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
89 &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
91 // Debugging helpers.
92 namespace {
94 std::string DisplayStatus(OM_uint32 major_status,
95 OM_uint32 minor_status) {
96 if (major_status == GSS_S_COMPLETE)
97 return "OK";
98 return base::StringPrintf("0x%08X 0x%08X", major_status, minor_status);
101 std::string DisplayCode(GSSAPILibrary* gssapi_lib,
102 OM_uint32 status,
103 OM_uint32 status_code_type) {
104 const int kMaxDisplayIterations = 8;
105 const size_t kMaxMsgLength = 4096;
106 // msg_ctx needs to be outside the loop because it is invoked multiple times.
107 OM_uint32 msg_ctx = 0;
108 std::string rv = base::StringPrintf("(0x%08X)", status);
110 // This loop should continue iterating until msg_ctx is 0 after the first
111 // iteration. To be cautious and prevent an infinite loop, it stops after
112 // a finite number of iterations as well. As an added sanity check, no
113 // individual message may exceed |kMaxMsgLength|, and the final result
114 // will not exceed |kMaxMsgLength|*2-1.
115 for (int i = 0; i < kMaxDisplayIterations && rv.size() < kMaxMsgLength;
116 ++i) {
117 OM_uint32 min_stat;
118 gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
119 OM_uint32 maj_stat =
120 gssapi_lib->display_status(&min_stat, status, status_code_type,
121 GSS_C_NULL_OID, &msg_ctx, &msg);
122 if (maj_stat == GSS_S_COMPLETE) {
123 int msg_len = (msg.length > kMaxMsgLength) ?
124 static_cast<int>(kMaxMsgLength) :
125 static_cast<int>(msg.length);
126 if (msg_len > 0 && msg.value != NULL) {
127 rv += base::StringPrintf(" %.*s", msg_len,
128 static_cast<char*>(msg.value));
131 gssapi_lib->release_buffer(&min_stat, &msg);
132 if (!msg_ctx)
133 break;
135 return rv;
138 std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
139 OM_uint32 major_status,
140 OM_uint32 minor_status) {
141 if (major_status == GSS_S_COMPLETE)
142 return "OK";
143 std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
144 std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
145 return base::StringPrintf("Major: %s | Minor: %s", major.c_str(),
146 minor.c_str());
149 // ScopedName releases a gss_name_t when it goes out of scope.
150 class ScopedName {
151 public:
152 ScopedName(gss_name_t name,
153 GSSAPILibrary* gssapi_lib)
154 : name_(name),
155 gssapi_lib_(gssapi_lib) {
156 DCHECK(gssapi_lib_);
159 ~ScopedName() {
160 if (name_ != GSS_C_NO_NAME) {
161 OM_uint32 minor_status = 0;
162 OM_uint32 major_status =
163 gssapi_lib_->release_name(&minor_status, &name_);
164 if (major_status != GSS_S_COMPLETE) {
165 LOG(WARNING) << "Problem releasing name. "
166 << DisplayStatus(major_status, minor_status);
168 name_ = GSS_C_NO_NAME;
172 private:
173 gss_name_t name_;
174 GSSAPILibrary* gssapi_lib_;
176 DISALLOW_COPY_AND_ASSIGN(ScopedName);
179 // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
180 class ScopedBuffer {
181 public:
182 ScopedBuffer(gss_buffer_t buffer,
183 GSSAPILibrary* gssapi_lib)
184 : buffer_(buffer),
185 gssapi_lib_(gssapi_lib) {
186 DCHECK(gssapi_lib_);
189 ~ScopedBuffer() {
190 if (buffer_ != GSS_C_NO_BUFFER) {
191 OM_uint32 minor_status = 0;
192 OM_uint32 major_status =
193 gssapi_lib_->release_buffer(&minor_status, buffer_);
194 if (major_status != GSS_S_COMPLETE) {
195 LOG(WARNING) << "Problem releasing buffer. "
196 << DisplayStatus(major_status, minor_status);
198 buffer_ = GSS_C_NO_BUFFER;
202 private:
203 gss_buffer_t buffer_;
204 GSSAPILibrary* gssapi_lib_;
206 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
209 namespace {
211 std::string AppendIfPredefinedValue(gss_OID oid,
212 gss_OID predefined_oid,
213 const char* predefined_oid_name) {
214 DCHECK(oid);
215 DCHECK(predefined_oid);
216 DCHECK(predefined_oid_name);
217 std::string output;
218 if (oid->length != predefined_oid->length)
219 return output;
220 if (0 != memcmp(oid->elements,
221 predefined_oid->elements,
222 predefined_oid->length))
223 return output;
225 output += " (";
226 output += predefined_oid_name;
227 output += ")";
228 return output;
231 } // namespace
233 std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) {
234 if (!oid)
235 return "<NULL>";
236 std::string output;
237 const size_t kMaxCharsToPrint = 1024;
238 OM_uint32 byte_length = oid->length;
239 size_t char_length = byte_length / sizeof(char);
240 if (char_length > kMaxCharsToPrint) {
241 // This might be a plain ASCII string.
242 // Check if the first |kMaxCharsToPrint| characters
243 // contain only printable characters and are NULL terminated.
244 const char* str = reinterpret_cast<const char*>(oid);
245 size_t str_length = 0;
246 for ( ; str_length < kMaxCharsToPrint; ++str_length) {
247 if (!str[str_length] || !isprint(str[str_length]))
248 break;
250 if (!str[str_length]) {
251 output += base::StringPrintf("\"%s\"", str);
252 return output;
255 output = base::StringPrintf("(%u) \"", byte_length);
256 if (!oid->elements) {
257 output += "<NULL>";
258 return output;
260 const unsigned char* elements =
261 reinterpret_cast<const unsigned char*>(oid->elements);
262 // Don't print more than |kMaxCharsToPrint| characters.
263 size_t i = 0;
264 for ( ; (i < byte_length) && (i < kMaxCharsToPrint); ++i) {
265 output += base::StringPrintf("\\x%02X", elements[i]);
267 if (i >= kMaxCharsToPrint)
268 output += "...";
269 output += "\"";
271 // Check if the OID is one of the predefined values.
272 output += AppendIfPredefinedValue(oid,
273 GSS_C_NT_USER_NAME,
274 "GSS_C_NT_USER_NAME");
275 output += AppendIfPredefinedValue(oid,
276 GSS_C_NT_MACHINE_UID_NAME,
277 "GSS_C_NT_MACHINE_UID_NAME");
278 output += AppendIfPredefinedValue(oid,
279 GSS_C_NT_STRING_UID_NAME,
280 "GSS_C_NT_STRING_UID_NAME");
281 output += AppendIfPredefinedValue(oid,
282 GSS_C_NT_HOSTBASED_SERVICE_X,
283 "GSS_C_NT_HOSTBASED_SERVICE_X");
284 output += AppendIfPredefinedValue(oid,
285 GSS_C_NT_HOSTBASED_SERVICE,
286 "GSS_C_NT_HOSTBASED_SERVICE");
287 output += AppendIfPredefinedValue(oid,
288 GSS_C_NT_ANONYMOUS,
289 "GSS_C_NT_ANONYMOUS");
290 output += AppendIfPredefinedValue(oid,
291 GSS_C_NT_EXPORT_NAME,
292 "GSS_C_NT_EXPORT_NAME");
294 return output;
297 std::string DescribeName(GSSAPILibrary* gssapi_lib, const gss_name_t name) {
298 OM_uint32 major_status = 0;
299 OM_uint32 minor_status = 0;
300 gss_buffer_desc_struct output_name_buffer = GSS_C_EMPTY_BUFFER;
301 gss_OID_desc output_name_type_desc = GSS_C_EMPTY_BUFFER;
302 gss_OID output_name_type = &output_name_type_desc;
303 major_status = gssapi_lib->display_name(&minor_status,
304 name,
305 &output_name_buffer,
306 &output_name_type);
307 ScopedBuffer scoped_output_name(&output_name_buffer, gssapi_lib);
308 if (major_status != GSS_S_COMPLETE) {
309 std::string error =
310 base::StringPrintf("Unable to describe name 0x%p, %s",
311 name,
312 DisplayExtendedStatus(gssapi_lib,
313 major_status,
314 minor_status).c_str());
315 return error;
317 int len = output_name_buffer.length;
318 std::string description = base::StringPrintf(
319 "%*s (Type %s)",
320 len,
321 reinterpret_cast<const char*>(output_name_buffer.value),
322 DescribeOid(gssapi_lib, output_name_type).c_str());
323 return description;
326 std::string DescribeContext(GSSAPILibrary* gssapi_lib,
327 const gss_ctx_id_t context_handle) {
328 OM_uint32 major_status = 0;
329 OM_uint32 minor_status = 0;
330 gss_name_t src_name = GSS_C_NO_NAME;
331 gss_name_t targ_name = GSS_C_NO_NAME;
332 OM_uint32 lifetime_rec = 0;
333 gss_OID mech_type = GSS_C_NO_OID;
334 OM_uint32 ctx_flags = 0;
335 int locally_initiated = 0;
336 int open = 0;
337 if (context_handle == GSS_C_NO_CONTEXT)
338 return std::string("Context: GSS_C_NO_CONTEXT");
339 major_status = gssapi_lib->inquire_context(&minor_status,
340 context_handle,
341 &src_name,
342 &targ_name,
343 &lifetime_rec,
344 &mech_type,
345 &ctx_flags,
346 &locally_initiated,
347 &open);
348 ScopedName scoped_src_name(src_name, gssapi_lib);
349 ScopedName scoped_targ_name(targ_name, gssapi_lib);
350 if (major_status != GSS_S_COMPLETE) {
351 std::string error =
352 base::StringPrintf("Unable to describe context 0x%p, %s",
353 context_handle,
354 DisplayExtendedStatus(gssapi_lib,
355 major_status,
356 minor_status).c_str());
357 return error;
359 std::string source(DescribeName(gssapi_lib, src_name));
360 std::string target(DescribeName(gssapi_lib, targ_name));
361 std::string description = base::StringPrintf("Context 0x%p: "
362 "Source \"%s\", "
363 "Target \"%s\", "
364 "lifetime %d, "
365 "mechanism %s, "
366 "flags 0x%08X, "
367 "local %d, "
368 "open %d",
369 context_handle,
370 source.c_str(),
371 target.c_str(),
372 lifetime_rec,
373 DescribeOid(gssapi_lib,
374 mech_type).c_str(),
375 ctx_flags,
376 locally_initiated,
377 open);
378 return description;
381 } // namespace
383 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name)
384 : initialized_(false),
385 gssapi_library_name_(gssapi_library_name),
386 gssapi_library_(NULL),
387 import_name_(NULL),
388 release_name_(NULL),
389 release_buffer_(NULL),
390 display_name_(NULL),
391 display_status_(NULL),
392 init_sec_context_(NULL),
393 wrap_size_limit_(NULL),
394 delete_sec_context_(NULL),
395 inquire_context_(NULL) {
398 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
399 if (gssapi_library_) {
400 base::UnloadNativeLibrary(gssapi_library_);
401 gssapi_library_ = NULL;
405 bool GSSAPISharedLibrary::Init() {
406 if (!initialized_)
407 InitImpl();
408 return initialized_;
411 bool GSSAPISharedLibrary::InitImpl() {
412 DCHECK(!initialized_);
413 #if defined(DLOPEN_KERBEROS)
414 gssapi_library_ = LoadSharedLibrary();
415 if (gssapi_library_ == NULL)
416 return false;
417 #endif // defined(DLOPEN_KERBEROS)
418 initialized_ = true;
419 return true;
422 base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() {
423 const char* const* library_names;
424 size_t num_lib_names;
425 const char* user_specified_library[1];
426 if (!gssapi_library_name_.empty()) {
427 user_specified_library[0] = gssapi_library_name_.c_str();
428 library_names = user_specified_library;
429 num_lib_names = 1;
430 } else {
431 static const char* const kDefaultLibraryNames[] = {
432 #if defined(OS_MACOSX)
433 // This library is provided by Kerberos.framework.
434 "libgssapi_krb5.dylib"
435 #elif defined(OS_OPENBSD)
436 "libgssapi.so" // Heimdal - OpenBSD
437 #else
438 "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian
439 "libgssapi.so.4", // Heimdal - Suse10, MDK
440 "libgssapi.so.2", // Heimdal - Gentoo
441 "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10
442 #endif
444 library_names = kDefaultLibraryNames;
445 num_lib_names = arraysize(kDefaultLibraryNames);
448 for (size_t i = 0; i < num_lib_names; ++i) {
449 const char* library_name = library_names[i];
450 base::FilePath file_path(library_name);
452 // TODO(asanka): Move library loading to a separate thread.
453 // http://crbug.com/66702
454 base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily;
455 base::NativeLibrary lib = base::LoadNativeLibrary(file_path, NULL);
456 if (lib) {
457 // Only return this library if we can bind the functions we need.
458 if (BindMethods(lib))
459 return lib;
460 base::UnloadNativeLibrary(lib);
463 LOG(WARNING) << "Unable to find a compatible GSSAPI library";
464 return NULL;
467 #if defined(DLOPEN_KERBEROS)
468 #define BIND(lib, x) \
469 DCHECK(lib); \
470 gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \
471 base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \
472 if (x == NULL) { \
473 LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \
474 return false; \
476 #else
477 #define BIND(lib, x) gss_##x##_type x = gss_##x
478 #endif
480 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) {
481 BIND(lib, import_name);
482 BIND(lib, release_name);
483 BIND(lib, release_buffer);
484 BIND(lib, display_name);
485 BIND(lib, display_status);
486 BIND(lib, init_sec_context);
487 BIND(lib, wrap_size_limit);
488 BIND(lib, delete_sec_context);
489 BIND(lib, inquire_context);
491 import_name_ = import_name;
492 release_name_ = release_name;
493 release_buffer_ = release_buffer;
494 display_name_ = display_name;
495 display_status_ = display_status;
496 init_sec_context_ = init_sec_context;
497 wrap_size_limit_ = wrap_size_limit;
498 delete_sec_context_ = delete_sec_context;
499 inquire_context_ = inquire_context;
501 return true;
504 #undef BIND
506 OM_uint32 GSSAPISharedLibrary::import_name(
507 OM_uint32* minor_status,
508 const gss_buffer_t input_name_buffer,
509 const gss_OID input_name_type,
510 gss_name_t* output_name) {
511 DCHECK(initialized_);
512 return import_name_(minor_status, input_name_buffer, input_name_type,
513 output_name);
516 OM_uint32 GSSAPISharedLibrary::release_name(
517 OM_uint32* minor_status,
518 gss_name_t* input_name) {
519 DCHECK(initialized_);
520 return release_name_(minor_status, input_name);
523 OM_uint32 GSSAPISharedLibrary::release_buffer(
524 OM_uint32* minor_status,
525 gss_buffer_t buffer) {
526 DCHECK(initialized_);
527 return release_buffer_(minor_status, buffer);
530 OM_uint32 GSSAPISharedLibrary::display_name(
531 OM_uint32* minor_status,
532 const gss_name_t input_name,
533 gss_buffer_t output_name_buffer,
534 gss_OID* output_name_type) {
535 DCHECK(initialized_);
536 return display_name_(minor_status,
537 input_name,
538 output_name_buffer,
539 output_name_type);
542 OM_uint32 GSSAPISharedLibrary::display_status(
543 OM_uint32* minor_status,
544 OM_uint32 status_value,
545 int status_type,
546 const gss_OID mech_type,
547 OM_uint32* message_context,
548 gss_buffer_t status_string) {
549 DCHECK(initialized_);
550 return display_status_(minor_status, status_value, status_type, mech_type,
551 message_context, status_string);
554 OM_uint32 GSSAPISharedLibrary::init_sec_context(
555 OM_uint32* minor_status,
556 const gss_cred_id_t initiator_cred_handle,
557 gss_ctx_id_t* context_handle,
558 const gss_name_t target_name,
559 const gss_OID mech_type,
560 OM_uint32 req_flags,
561 OM_uint32 time_req,
562 const gss_channel_bindings_t input_chan_bindings,
563 const gss_buffer_t input_token,
564 gss_OID* actual_mech_type,
565 gss_buffer_t output_token,
566 OM_uint32* ret_flags,
567 OM_uint32* time_rec) {
568 DCHECK(initialized_);
569 return init_sec_context_(minor_status,
570 initiator_cred_handle,
571 context_handle,
572 target_name,
573 mech_type,
574 req_flags,
575 time_req,
576 input_chan_bindings,
577 input_token,
578 actual_mech_type,
579 output_token,
580 ret_flags,
581 time_rec);
584 OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
585 OM_uint32* minor_status,
586 const gss_ctx_id_t context_handle,
587 int conf_req_flag,
588 gss_qop_t qop_req,
589 OM_uint32 req_output_size,
590 OM_uint32* max_input_size) {
591 DCHECK(initialized_);
592 return wrap_size_limit_(minor_status,
593 context_handle,
594 conf_req_flag,
595 qop_req,
596 req_output_size,
597 max_input_size);
600 OM_uint32 GSSAPISharedLibrary::delete_sec_context(
601 OM_uint32* minor_status,
602 gss_ctx_id_t* context_handle,
603 gss_buffer_t output_token) {
604 // This is called from the owner class' destructor, even if
605 // Init() is not called, so we can't assume |initialized_|
606 // is set.
607 if (!initialized_)
608 return 0;
609 return delete_sec_context_(minor_status,
610 context_handle,
611 output_token);
614 OM_uint32 GSSAPISharedLibrary::inquire_context(
615 OM_uint32* minor_status,
616 const gss_ctx_id_t context_handle,
617 gss_name_t* src_name,
618 gss_name_t* targ_name,
619 OM_uint32* lifetime_rec,
620 gss_OID* mech_type,
621 OM_uint32* ctx_flags,
622 int* locally_initiated,
623 int* open) {
624 DCHECK(initialized_);
625 return inquire_context_(minor_status,
626 context_handle,
627 src_name,
628 targ_name,
629 lifetime_rec,
630 mech_type,
631 ctx_flags,
632 locally_initiated,
633 open);
636 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
637 : security_context_(GSS_C_NO_CONTEXT),
638 gssapi_lib_(gssapi_lib) {
639 DCHECK(gssapi_lib_);
642 ScopedSecurityContext::~ScopedSecurityContext() {
643 if (security_context_ != GSS_C_NO_CONTEXT) {
644 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
645 OM_uint32 minor_status = 0;
646 OM_uint32 major_status = gssapi_lib_->delete_sec_context(
647 &minor_status, &security_context_, &output_token);
648 if (major_status != GSS_S_COMPLETE) {
649 LOG(WARNING) << "Problem releasing security_context. "
650 << DisplayStatus(major_status, minor_status);
652 security_context_ = GSS_C_NO_CONTEXT;
656 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
657 const std::string& scheme,
658 gss_OID gss_oid)
659 : scheme_(scheme),
660 gss_oid_(gss_oid),
661 library_(library),
662 scoped_sec_context_(library),
663 can_delegate_(false) {
664 DCHECK(library_);
667 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
670 bool HttpAuthGSSAPI::Init() {
671 if (!library_)
672 return false;
673 return library_->Init();
676 bool HttpAuthGSSAPI::NeedsIdentity() const {
677 return decoded_server_auth_token_.empty();
680 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
681 return false;
684 void HttpAuthGSSAPI::Delegate() {
685 can_delegate_ = true;
688 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
689 HttpAuthChallengeTokenizer* tok) {
690 // Verify the challenge's auth-scheme.
691 if (!LowerCaseEqualsASCII(tok->scheme(),
692 base::StringToLowerASCII(scheme_).c_str()))
693 return HttpAuth::AUTHORIZATION_RESULT_INVALID;
695 std::string encoded_auth_token = tok->base64_param();
697 if (encoded_auth_token.empty()) {
698 // If a context has already been established, an empty Negotiate challenge
699 // should be treated as a rejection of the current attempt.
700 if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT)
701 return HttpAuth::AUTHORIZATION_RESULT_REJECT;
702 DCHECK(decoded_server_auth_token_.empty());
703 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
704 } else {
705 // If a context has not already been established, additional tokens should
706 // not be present in the auth challenge.
707 if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT)
708 return HttpAuth::AUTHORIZATION_RESULT_INVALID;
711 // Make sure the additional token is base64 encoded.
712 std::string decoded_auth_token;
713 bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
714 if (!base64_rv)
715 return HttpAuth::AUTHORIZATION_RESULT_INVALID;
716 decoded_server_auth_token_ = decoded_auth_token;
717 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
720 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
721 const std::string& spn,
722 std::string* auth_token) {
723 DCHECK(auth_token);
725 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
726 input_token.length = decoded_server_auth_token_.length();
727 input_token.value = (input_token.length > 0) ?
728 const_cast<char*>(decoded_server_auth_token_.data()) :
729 NULL;
730 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
731 ScopedBuffer scoped_output_token(&output_token, library_);
732 int rv = GetNextSecurityToken(spn, &input_token, &output_token);
733 if (rv != OK)
734 return rv;
736 // Base64 encode data in output buffer and prepend the scheme.
737 std::string encode_input(static_cast<char*>(output_token.value),
738 output_token.length);
739 std::string encode_output;
740 base::Base64Encode(encode_input, &encode_output);
741 *auth_token = scheme_ + " " + encode_output;
742 return OK;
746 namespace {
748 // GSSAPI status codes consist of a calling error (essentially, a programmer
749 // bug), a routine error (defined by the RFC), and supplementary information,
750 // all bitwise-or'ed together in different regions of the 32 bit return value.
751 // This means a simple switch on the return codes is not sufficient.
753 int MapImportNameStatusToError(OM_uint32 major_status) {
754 VLOG(1) << "import_name returned 0x" << std::hex << major_status;
755 if (major_status == GSS_S_COMPLETE)
756 return OK;
757 if (GSS_CALLING_ERROR(major_status) != 0)
758 return ERR_UNEXPECTED;
759 OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
760 switch (routine_error) {
761 case GSS_S_FAILURE:
762 // Looking at the MIT Kerberos implementation, this typically is returned
763 // when memory allocation fails. However, the API does not guarantee
764 // that this is the case, so using ERR_UNEXPECTED rather than
765 // ERR_OUT_OF_MEMORY.
766 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
767 case GSS_S_BAD_NAME:
768 case GSS_S_BAD_NAMETYPE:
769 return ERR_MALFORMED_IDENTITY;
770 case GSS_S_DEFECTIVE_TOKEN:
771 // Not mentioned in the API, but part of code.
772 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
773 case GSS_S_BAD_MECH:
774 return ERR_UNSUPPORTED_AUTH_SCHEME;
775 default:
776 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
780 int MapInitSecContextStatusToError(OM_uint32 major_status) {
781 VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status;
782 // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
783 // other code just checks if major_status is equivalent to it to indicate
784 // that there are no other errors included.
785 if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
786 return OK;
787 if (GSS_CALLING_ERROR(major_status) != 0)
788 return ERR_UNEXPECTED;
789 OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
790 switch (routine_status) {
791 case GSS_S_DEFECTIVE_TOKEN:
792 return ERR_INVALID_RESPONSE;
793 case GSS_S_DEFECTIVE_CREDENTIAL:
794 // Not expected since this implementation uses the default credential.
795 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
796 case GSS_S_BAD_SIG:
797 // Probably won't happen, but it's a bad response.
798 return ERR_INVALID_RESPONSE;
799 case GSS_S_NO_CRED:
800 return ERR_INVALID_AUTH_CREDENTIALS;
801 case GSS_S_CREDENTIALS_EXPIRED:
802 return ERR_INVALID_AUTH_CREDENTIALS;
803 case GSS_S_BAD_BINDINGS:
804 // This only happens with mutual authentication.
805 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
806 case GSS_S_NO_CONTEXT:
807 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
808 case GSS_S_BAD_NAMETYPE:
809 return ERR_UNSUPPORTED_AUTH_SCHEME;
810 case GSS_S_BAD_NAME:
811 return ERR_UNSUPPORTED_AUTH_SCHEME;
812 case GSS_S_BAD_MECH:
813 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
814 case GSS_S_FAILURE:
815 // This should be an "Unexpected Security Status" according to the
816 // GSSAPI documentation, but it's typically used to indicate that
817 // credentials are not correctly set up on a user machine, such
818 // as a missing credential cache or hitting this after calling
819 // kdestroy.
820 // TODO(cbentzel): Use minor code for even better mapping?
821 return ERR_MISSING_AUTH_CREDENTIALS;
822 default:
823 if (routine_status != 0)
824 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
825 break;
827 OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
828 // Replays could indicate an attack.
829 if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
830 GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
831 return ERR_INVALID_RESPONSE;
833 // At this point, every documented status has been checked.
834 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
839 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
840 gss_buffer_t in_token,
841 gss_buffer_t out_token) {
842 // Create a name for the principal
843 // TODO(cbentzel): Just do this on the first pass?
844 std::string spn_principal = spn;
845 gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
846 spn_buffer.value = const_cast<char*>(spn_principal.c_str());
847 spn_buffer.length = spn_principal.size() + 1;
848 OM_uint32 minor_status = 0;
849 gss_name_t principal_name = GSS_C_NO_NAME;
850 OM_uint32 major_status = library_->import_name(
851 &minor_status,
852 &spn_buffer,
853 GSS_C_NT_HOSTBASED_SERVICE,
854 &principal_name);
855 int rv = MapImportNameStatusToError(major_status);
856 if (rv != OK) {
857 LOG(ERROR) << "Problem importing name from "
858 << "spn \"" << spn_principal << "\"\n"
859 << DisplayExtendedStatus(library_, major_status, minor_status);
860 return rv;
862 ScopedName scoped_name(principal_name, library_);
864 // Continue creating a security context.
865 OM_uint32 req_flags = 0;
866 if (can_delegate_)
867 req_flags |= GSS_C_DELEG_FLAG;
868 major_status = library_->init_sec_context(
869 &minor_status,
870 GSS_C_NO_CREDENTIAL,
871 scoped_sec_context_.receive(),
872 principal_name,
873 gss_oid_,
874 req_flags,
875 GSS_C_INDEFINITE,
876 GSS_C_NO_CHANNEL_BINDINGS,
877 in_token,
878 NULL, // actual_mech_type
879 out_token,
880 NULL, // ret flags
881 NULL);
882 rv = MapInitSecContextStatusToError(major_status);
883 if (rv != OK) {
884 LOG(ERROR) << "Problem initializing context. \n"
885 << DisplayExtendedStatus(library_, major_status, minor_status)
886 << '\n'
887 << DescribeContext(library_, scoped_sec_context_.get());
889 return rv;
892 } // namespace net