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