Home | History | Annotate | Download | only in http
      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 <limits>
      8 #include <string>
      9 
     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"
     20 
     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 {
     28 
     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")
     32 };
     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")
     36 };
     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")
     40 };
     41 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
     42   6,
     43   const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
     44 };
     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")
     48 };
     49 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = {
     50   6,
     51   const_cast<char*>("\x2b\x06\01\x05\x06\x03")
     52 };
     53 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = {
     54   6,
     55   const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
     56 };
     57 
     58 }  // namespace
     59 
     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
     69 
     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;
     77 
     78 namespace net {
     79 
     80 // Exported mechanism for GSSAPI. We always use SPNEGO:
     81 
     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 = {
     84   6,
     85   const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
     86 };
     87 
     88 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
     89     &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
     90 
     91 // Debugging helpers.
     92 namespace {
     93 
     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);
     99 }
    100 
    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);
    109 
    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));
    129       }
    130     }
    131     gssapi_lib->release_buffer(&min_stat, &msg);
    132     if (!msg_ctx)
    133       break;
    134   }
    135   return rv;
    136 }
    137 
    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());
    147 }
    148 
    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_);
    157   }
    158 
    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);
    167       }
    168       name_ = GSS_C_NO_NAME;
    169     }
    170   }
    171 
    172  private:
    173   gss_name_t name_;
    174   GSSAPILibrary* gssapi_lib_;
    175 
    176   DISALLOW_COPY_AND_ASSIGN(ScopedName);
    177 };
    178 
    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_);
    187   }
    188 
    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);
    197       }
    198       buffer_ = GSS_C_NO_BUFFER;
    199     }
    200   }
    201 
    202  private:
    203   gss_buffer_t buffer_;
    204   GSSAPILibrary* gssapi_lib_;
    205 
    206   DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
    207 };
    208 
    209 namespace {
    210 
    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;
    224 
    225   output += " (";
    226   output += predefined_oid_name;
    227   output += ")";
    228   return output;
    229 }
    230 
    231 }  // namespace
    232 
    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;
    249     }
    250     if (!str[str_length]) {
    251       output += base::StringPrintf("\"%s\"", str);
    252       return output;
    253     }
    254   }
    255   output = base::StringPrintf("(%u) \"", byte_length);
    256   if (!oid->elements) {
    257     output += "<NULL>";
    258     return output;
    259   }
    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]);
    266   }
    267   if (i >= kMaxCharsToPrint)
    268     output += "...";
    269   output += "\"";
    270 
    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");
    293 
    294   return output;
    295 }
    296 
    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;
    316   }
    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;
    324 }
    325 
    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(src_name, gssapi_lib);
    349   ScopedName(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;
    358   }
    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;
    379 }
    380 
    381 }  // namespace
    382 
    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) {
    396 }
    397 
    398 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
    399   if (gssapi_library_) {
    400     base::UnloadNativeLibrary(gssapi_library_);
    401     gssapi_library_ = NULL;
    402   }
    403 }
    404 
    405 bool GSSAPISharedLibrary::Init() {
    406   if (!initialized_)
    407     InitImpl();
    408   return initialized_;
    409 }
    410 
    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;
    420 }
    421 
    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       "libgssapi_krb5.dylib"  // MIT Kerberos
    434 #elif defined(OS_OPENBSD)
    435       "libgssapi.so"          // Heimdal - OpenBSD
    436 #else
    437       "libgssapi_krb5.so.2",  // MIT Kerberos - FC, Suse10, Debian
    438       "libgssapi.so.4",       // Heimdal - Suse10, MDK
    439       "libgssapi.so.2",       // Heimdal - Gentoo
    440       "libgssapi.so.1"        // Heimdal - Suse9, CITI - FC, MDK, Suse10
    441 #endif
    442     };
    443     library_names = kDefaultLibraryNames;
    444     num_lib_names = arraysize(kDefaultLibraryNames);
    445   }
    446 
    447   for (size_t i = 0; i < num_lib_names; ++i) {
    448     const char* library_name = library_names[i];
    449     base::FilePath file_path(library_name);
    450 
    451     // TODO(asanka): Move library loading to a separate thread.
    452     //               http://crbug.com/66702
    453     base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily;
    454     base::NativeLibrary lib = base::LoadNativeLibrary(file_path, NULL);
    455     if (lib) {
    456       // Only return this library if we can bind the functions we need.
    457       if (BindMethods(lib))
    458         return lib;
    459       base::UnloadNativeLibrary(lib);
    460     }
    461   }
    462   LOG(WARNING) << "Unable to find a compatible GSSAPI library";
    463   return NULL;
    464 }
    465 
    466 #if defined(DLOPEN_KERBEROS)
    467 #define BIND(lib, x)                                                    \
    468   DCHECK(lib);                                                          \
    469   gss_##x##_type x = reinterpret_cast<gss_##x##_type>(                  \
    470       base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x));       \
    471   if (x == NULL) {                                                      \
    472     LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\"";  \
    473     return false;                                                       \
    474   }
    475 #else
    476 #define BIND(lib, x) gss_##x##_type x = gss_##x
    477 #endif
    478 
    479 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) {
    480   BIND(lib, import_name);
    481   BIND(lib, release_name);
    482   BIND(lib, release_buffer);
    483   BIND(lib, display_name);
    484   BIND(lib, display_status);
    485   BIND(lib, init_sec_context);
    486   BIND(lib, wrap_size_limit);
    487   BIND(lib, delete_sec_context);
    488   BIND(lib, inquire_context);
    489 
    490   import_name_ = import_name;
    491   release_name_ = release_name;
    492   release_buffer_ = release_buffer;
    493   display_name_ = display_name;
    494   display_status_ = display_status;
    495   init_sec_context_ = init_sec_context;
    496   wrap_size_limit_ = wrap_size_limit;
    497   delete_sec_context_ = delete_sec_context;
    498   inquire_context_ = inquire_context;
    499 
    500   return true;
    501 }
    502 
    503 #undef BIND
    504 
    505 OM_uint32 GSSAPISharedLibrary::import_name(
    506     OM_uint32* minor_status,
    507     const gss_buffer_t input_name_buffer,
    508     const gss_OID input_name_type,
    509     gss_name_t* output_name) {
    510   DCHECK(initialized_);
    511   return import_name_(minor_status, input_name_buffer, input_name_type,
    512                       output_name);
    513 }
    514 
    515 OM_uint32 GSSAPISharedLibrary::release_name(
    516     OM_uint32* minor_status,
    517     gss_name_t* input_name) {
    518   DCHECK(initialized_);
    519   return release_name_(minor_status, input_name);
    520 }
    521 
    522 OM_uint32 GSSAPISharedLibrary::release_buffer(
    523     OM_uint32* minor_status,
    524     gss_buffer_t buffer) {
    525   DCHECK(initialized_);
    526   return release_buffer_(minor_status, buffer);
    527 }
    528 
    529 OM_uint32 GSSAPISharedLibrary::display_name(
    530     OM_uint32* minor_status,
    531     const gss_name_t input_name,
    532     gss_buffer_t output_name_buffer,
    533     gss_OID* output_name_type) {
    534   DCHECK(initialized_);
    535   return display_name_(minor_status,
    536                        input_name,
    537                        output_name_buffer,
    538                        output_name_type);
    539 }
    540 
    541 OM_uint32 GSSAPISharedLibrary::display_status(
    542     OM_uint32* minor_status,
    543     OM_uint32 status_value,
    544     int status_type,
    545     const gss_OID mech_type,
    546     OM_uint32* message_context,
    547     gss_buffer_t status_string) {
    548   DCHECK(initialized_);
    549   return display_status_(minor_status, status_value, status_type, mech_type,
    550                          message_context, status_string);
    551 }
    552 
    553 OM_uint32 GSSAPISharedLibrary::init_sec_context(
    554     OM_uint32* minor_status,
    555     const gss_cred_id_t initiator_cred_handle,
    556     gss_ctx_id_t* context_handle,
    557     const gss_name_t target_name,
    558     const gss_OID mech_type,
    559     OM_uint32 req_flags,
    560     OM_uint32 time_req,
    561     const gss_channel_bindings_t input_chan_bindings,
    562     const gss_buffer_t input_token,
    563     gss_OID* actual_mech_type,
    564     gss_buffer_t output_token,
    565     OM_uint32* ret_flags,
    566     OM_uint32* time_rec) {
    567   DCHECK(initialized_);
    568   return init_sec_context_(minor_status,
    569                            initiator_cred_handle,
    570                            context_handle,
    571                            target_name,
    572                            mech_type,
    573                            req_flags,
    574                            time_req,
    575                            input_chan_bindings,
    576                            input_token,
    577                            actual_mech_type,
    578                            output_token,
    579                            ret_flags,
    580                            time_rec);
    581 }
    582 
    583 OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
    584     OM_uint32* minor_status,
    585     const gss_ctx_id_t context_handle,
    586     int conf_req_flag,
    587     gss_qop_t qop_req,
    588     OM_uint32 req_output_size,
    589     OM_uint32* max_input_size) {
    590   DCHECK(initialized_);
    591   return wrap_size_limit_(minor_status,
    592                           context_handle,
    593                           conf_req_flag,
    594                           qop_req,
    595                           req_output_size,
    596                           max_input_size);
    597 }
    598 
    599 OM_uint32 GSSAPISharedLibrary::delete_sec_context(
    600     OM_uint32* minor_status,
    601     gss_ctx_id_t* context_handle,
    602     gss_buffer_t output_token) {
    603   // This is called from the owner class' destructor, even if
    604   // Init() is not called, so we can't assume |initialized_|
    605   // is set.
    606   if (!initialized_)
    607     return 0;
    608   return delete_sec_context_(minor_status,
    609                              context_handle,
    610                              output_token);
    611 }
    612 
    613 OM_uint32 GSSAPISharedLibrary::inquire_context(
    614     OM_uint32* minor_status,
    615     const gss_ctx_id_t context_handle,
    616     gss_name_t* src_name,
    617     gss_name_t* targ_name,
    618     OM_uint32* lifetime_rec,
    619     gss_OID* mech_type,
    620     OM_uint32* ctx_flags,
    621     int* locally_initiated,
    622     int* open) {
    623   DCHECK(initialized_);
    624   return inquire_context_(minor_status,
    625                           context_handle,
    626                           src_name,
    627                           targ_name,
    628                           lifetime_rec,
    629                           mech_type,
    630                           ctx_flags,
    631                           locally_initiated,
    632                           open);
    633 }
    634 
    635 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
    636     : security_context_(GSS_C_NO_CONTEXT),
    637       gssapi_lib_(gssapi_lib) {
    638   DCHECK(gssapi_lib_);
    639 }
    640 
    641 ScopedSecurityContext::~ScopedSecurityContext() {
    642   if (security_context_ != GSS_C_NO_CONTEXT) {
    643     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
    644     OM_uint32 minor_status = 0;
    645     OM_uint32 major_status = gssapi_lib_->delete_sec_context(
    646         &minor_status, &security_context_, &output_token);
    647     if (major_status != GSS_S_COMPLETE) {
    648       LOG(WARNING) << "Problem releasing security_context. "
    649                    << DisplayStatus(major_status, minor_status);
    650     }
    651     security_context_ = GSS_C_NO_CONTEXT;
    652   }
    653 }
    654 
    655 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
    656                                const std::string& scheme,
    657                                gss_OID gss_oid)
    658     : scheme_(scheme),
    659       gss_oid_(gss_oid),
    660       library_(library),
    661       scoped_sec_context_(library),
    662       can_delegate_(false) {
    663   DCHECK(library_);
    664 }
    665 
    666 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
    667 }
    668 
    669 bool HttpAuthGSSAPI::Init() {
    670   if (!library_)
    671     return false;
    672   return library_->Init();
    673 }
    674 
    675 bool HttpAuthGSSAPI::NeedsIdentity() const {
    676   return decoded_server_auth_token_.empty();
    677 }
    678 
    679 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
    680   return false;
    681 }
    682 
    683 void HttpAuthGSSAPI::Delegate() {
    684   can_delegate_ = true;
    685 }
    686 
    687 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
    688     HttpAuthChallengeTokenizer* tok) {
    689   // Verify the challenge's auth-scheme.
    690   if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
    691     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
    692 
    693   std::string encoded_auth_token = tok->base64_param();
    694 
    695   if (encoded_auth_token.empty()) {
    696     // If a context has already been established, an empty Negotiate challenge
    697     // should be treated as a rejection of the current attempt.
    698     if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT)
    699       return HttpAuth::AUTHORIZATION_RESULT_REJECT;
    700     DCHECK(decoded_server_auth_token_.empty());
    701     return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
    702   } else {
    703     // If a context has not already been established, additional tokens should
    704     // not be present in the auth challenge.
    705     if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT)
    706       return HttpAuth::AUTHORIZATION_RESULT_INVALID;
    707   }
    708 
    709   // Make sure the additional token is base64 encoded.
    710   std::string decoded_auth_token;
    711   bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
    712   if (!base64_rv)
    713     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
    714   decoded_server_auth_token_ = decoded_auth_token;
    715   return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
    716 }
    717 
    718 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
    719                                       const std::string& spn,
    720                                       std::string* auth_token) {
    721   DCHECK(auth_token);
    722 
    723   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
    724   input_token.length = decoded_server_auth_token_.length();
    725   input_token.value = (input_token.length > 0) ?
    726       const_cast<char*>(decoded_server_auth_token_.data()) :
    727       NULL;
    728   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
    729   ScopedBuffer scoped_output_token(&output_token, library_);
    730   int rv = GetNextSecurityToken(spn, &input_token, &output_token);
    731   if (rv != OK)
    732     return rv;
    733 
    734   // Base64 encode data in output buffer and prepend the scheme.
    735   std::string encode_input(static_cast<char*>(output_token.value),
    736                            output_token.length);
    737   std::string encode_output;
    738   base::Base64Encode(encode_input, &encode_output);
    739   *auth_token = scheme_ + " " + encode_output;
    740   return OK;
    741 }
    742 
    743 
    744 namespace {
    745 
    746 // GSSAPI status codes consist of a calling error (essentially, a programmer
    747 // bug), a routine error (defined by the RFC), and supplementary information,
    748 // all bitwise-or'ed together in different regions of the 32 bit return value.
    749 // This means a simple switch on the return codes is not sufficient.
    750 
    751 int MapImportNameStatusToError(OM_uint32 major_status) {
    752   VLOG(1) << "import_name returned 0x" << std::hex << major_status;
    753   if (major_status == GSS_S_COMPLETE)
    754     return OK;
    755   if (GSS_CALLING_ERROR(major_status) != 0)
    756     return ERR_UNEXPECTED;
    757   OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
    758   switch (routine_error) {
    759     case GSS_S_FAILURE:
    760       // Looking at the MIT Kerberos implementation, this typically is returned
    761       // when memory allocation fails. However, the API does not guarantee
    762       // that this is the case, so using ERR_UNEXPECTED rather than
    763       // ERR_OUT_OF_MEMORY.
    764       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    765     case GSS_S_BAD_NAME:
    766     case GSS_S_BAD_NAMETYPE:
    767       return ERR_MALFORMED_IDENTITY;
    768     case GSS_S_DEFECTIVE_TOKEN:
    769       // Not mentioned in the API, but part of code.
    770       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    771     case GSS_S_BAD_MECH:
    772       return ERR_UNSUPPORTED_AUTH_SCHEME;
    773     default:
    774       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    775   }
    776 }
    777 
    778 int MapInitSecContextStatusToError(OM_uint32 major_status) {
    779   VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status;
    780   // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
    781   // other code just checks if major_status is equivalent to it to indicate
    782   // that there are no other errors included.
    783   if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
    784     return OK;
    785   if (GSS_CALLING_ERROR(major_status) != 0)
    786     return ERR_UNEXPECTED;
    787   OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
    788   switch (routine_status) {
    789     case GSS_S_DEFECTIVE_TOKEN:
    790       return ERR_INVALID_RESPONSE;
    791     case GSS_S_DEFECTIVE_CREDENTIAL:
    792       // Not expected since this implementation uses the default credential.
    793       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    794     case GSS_S_BAD_SIG:
    795       // Probably won't happen, but it's a bad response.
    796       return ERR_INVALID_RESPONSE;
    797     case GSS_S_NO_CRED:
    798       return ERR_INVALID_AUTH_CREDENTIALS;
    799     case GSS_S_CREDENTIALS_EXPIRED:
    800       return ERR_INVALID_AUTH_CREDENTIALS;
    801     case GSS_S_BAD_BINDINGS:
    802       // This only happens with mutual authentication.
    803       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    804     case GSS_S_NO_CONTEXT:
    805       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    806     case GSS_S_BAD_NAMETYPE:
    807       return ERR_UNSUPPORTED_AUTH_SCHEME;
    808     case GSS_S_BAD_NAME:
    809       return ERR_UNSUPPORTED_AUTH_SCHEME;
    810     case GSS_S_BAD_MECH:
    811       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    812     case GSS_S_FAILURE:
    813       // This should be an "Unexpected Security Status" according to the
    814       // GSSAPI documentation, but it's typically used to indicate that
    815       // credentials are not correctly set up on a user machine, such
    816       // as a missing credential cache or hitting this after calling
    817       // kdestroy.
    818       // TODO(cbentzel): Use minor code for even better mapping?
    819       return ERR_MISSING_AUTH_CREDENTIALS;
    820     default:
    821       if (routine_status != 0)
    822         return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    823       break;
    824   }
    825   OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
    826   // Replays could indicate an attack.
    827   if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
    828                              GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
    829     return ERR_INVALID_RESPONSE;
    830 
    831   // At this point, every documented status has been checked.
    832   return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    833 }
    834 
    835 }
    836 
    837 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
    838                                          gss_buffer_t in_token,
    839                                          gss_buffer_t out_token) {
    840   // Create a name for the principal
    841   // TODO(cbentzel): Just do this on the first pass?
    842   std::string spn_principal = spn;
    843   gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
    844   spn_buffer.value = const_cast<char*>(spn_principal.c_str());
    845   spn_buffer.length = spn_principal.size() + 1;
    846   OM_uint32 minor_status = 0;
    847   gss_name_t principal_name = GSS_C_NO_NAME;
    848   OM_uint32 major_status = library_->import_name(
    849       &minor_status,
    850       &spn_buffer,
    851       GSS_C_NT_HOSTBASED_SERVICE,
    852       &principal_name);
    853   int rv = MapImportNameStatusToError(major_status);
    854   if (rv != OK) {
    855     LOG(ERROR) << "Problem importing name from "
    856                << "spn \"" << spn_principal << "\"\n"
    857                << DisplayExtendedStatus(library_, major_status, minor_status);
    858     return rv;
    859   }
    860   ScopedName scoped_name(principal_name, library_);
    861 
    862   // Continue creating a security context.
    863   OM_uint32 req_flags = 0;
    864   if (can_delegate_)
    865     req_flags |= GSS_C_DELEG_FLAG;
    866   major_status = library_->init_sec_context(
    867       &minor_status,
    868       GSS_C_NO_CREDENTIAL,
    869       scoped_sec_context_.receive(),
    870       principal_name,
    871       gss_oid_,
    872       req_flags,
    873       GSS_C_INDEFINITE,
    874       GSS_C_NO_CHANNEL_BINDINGS,
    875       in_token,
    876       NULL,  // actual_mech_type
    877       out_token,
    878       NULL,  // ret flags
    879       NULL);
    880   rv = MapInitSecContextStatusToError(major_status);
    881   if (rv != OK) {
    882     LOG(ERROR) << "Problem initializing context. \n"
    883                << DisplayExtendedStatus(library_, major_status, minor_status)
    884                << '\n'
    885                << DescribeContext(library_, scoped_sec_context_.get());
    886   }
    887   return rv;
    888 }
    889 
    890 }  // namespace net
    891