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 
     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 {
     27 
     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")
     31 };
     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")
     35 };
     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")
     39 };
     40 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
     41   6,
     42   const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
     43 };
     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")
     47 };
     48 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = {
     49   6,
     50   const_cast<char*>("\x2b\x06\01\x05\x06\x03")
     51 };
     52 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = {
     53   6,
     54   const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
     55 };
     56 
     57 }  // namespace
     58 
     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
     68 
     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;
     76 
     77 namespace net {
     78 
     79 // Exported mechanism for GSSAPI. We always use SPNEGO:
     80 
     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 = {
     83   6,
     84   const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
     85 };
     86 
     87 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
     88     &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
     89 
     90 // Debugging helpers.
     91 namespace {
     92 
     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);
     98 }
     99 
    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);
    108 
    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));
    128       }
    129     }
    130     gssapi_lib->release_buffer(&min_stat, &msg);
    131     if (!msg_ctx)
    132       break;
    133   }
    134   return rv;
    135 }
    136 
    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());
    146 }
    147 
    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_);
    156   }
    157 
    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);
    166       }
    167       name_ = GSS_C_NO_NAME;
    168     }
    169   }
    170 
    171  private:
    172   gss_name_t name_;
    173   GSSAPILibrary* gssapi_lib_;
    174 
    175   DISALLOW_COPY_AND_ASSIGN(ScopedName);
    176 };
    177 
    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_);
    186   }
    187 
    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);
    196       }
    197       buffer_ = GSS_C_NO_BUFFER;
    198     }
    199   }
    200 
    201  private:
    202   gss_buffer_t buffer_;
    203   GSSAPILibrary* gssapi_lib_;
    204 
    205   DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
    206 };
    207 
    208 namespace {
    209 
    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;
    223 
    224   output += " (";
    225   output += predefined_oid_name;
    226   output += ")";
    227   return output;
    228 }
    229 
    230 }  // namespace
    231 
    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;
    248     }
    249     if (!str[str_length]) {
    250       output += base::StringPrintf("\"%s\"", str);
    251       return output;
    252     }
    253   }
    254   output = base::StringPrintf("(%u) \"", byte_length);
    255   if (!oid->elements) {
    256     output += "<NULL>";
    257     return output;
    258   }
    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]);
    265   }
    266   if (i >= kMaxCharsToPrint)
    267     output += "...";
    268   output += "\"";
    269 
    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");
    292 
    293   return output;
    294 }
    295 
    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;
    315   }
    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;
    323 }
    324 
    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;
    357   }
    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;
    378 }
    379 
    380 }  // namespace
    381 
    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) {
    395 }
    396 
    397 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
    398   if (gssapi_library_) {
    399     base::UnloadNativeLibrary(gssapi_library_);
    400     gssapi_library_ = NULL;
    401   }
    402 }
    403 
    404 bool GSSAPISharedLibrary::Init() {
    405   if (!initialized_)
    406     InitImpl();
    407   return initialized_;
    408 }
    409 
    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;
    419 }
    420 
    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
    441     };
    442     library_names = kDefaultLibraryNames;
    443     num_lib_names = arraysize(kDefaultLibraryNames);
    444   }
    445 
    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);
    449 
    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);
    459     }
    460   }
    461   LOG(WARNING) << "Unable to find a compatible GSSAPI library";
    462   return NULL;
    463 }
    464 
    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;                                                       \
    473   }
    474 #else
    475 #define BIND(lib, x) gss_##x##_type x = gss_##x
    476 #endif
    477 
    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);
    488 
    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;
    498 
    499   return true;
    500 }
    501 
    502 #undef BIND
    503 
    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);
    512 }
    513 
    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);
    519 }
    520 
    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);
    526 }
    527 
    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);
    538 }
    539 
    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);
    550 }
    551 
    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);
    580 }
    581 
    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);
    596 }
    597 
    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);
    610 }
    611 
    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);
    632 }
    633 
    634 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
    635     : security_context_(GSS_C_NO_CONTEXT),
    636       gssapi_lib_(gssapi_lib) {
    637   DCHECK(gssapi_lib_);
    638 }
    639 
    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);
    649     }
    650     security_context_ = GSS_C_NO_CONTEXT;
    651   }
    652 }
    653 
    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_);
    663 }
    664 
    665 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
    666 }
    667 
    668 bool HttpAuthGSSAPI::Init() {
    669   if (!library_)
    670     return false;
    671   return library_->Init();
    672 }
    673 
    674 bool HttpAuthGSSAPI::NeedsIdentity() const {
    675   return decoded_server_auth_token_.empty();
    676 }
    677 
    678 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
    679   return false;
    680 }
    681 
    682 void HttpAuthGSSAPI::Delegate() {
    683   can_delegate_ = true;
    684 }
    685 
    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;
    691 
    692   std::string encoded_auth_token = tok->base64_param();
    693 
    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;
    706   }
    707 
    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;
    715 }
    716 
    717 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
    718                                       const std::wstring& spn,
    719                                       std::string* auth_token) {
    720   DCHECK(auth_token);
    721 
    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;
    732 
    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   bool base64_rv = base::Base64Encode(encode_input, &encode_output);
    738   if (!base64_rv) {
    739     LOG(ERROR) << "Base64 encoding of auth token failed.";
    740     return ERR_ENCODING_CONVERSION_FAILED;
    741   }
    742   *auth_token = scheme_ + " " + encode_output;
    743   return OK;
    744 }
    745 
    746 
    747 namespace {
    748 
    749 // GSSAPI status codes consist of a calling error (essentially, a programmer
    750 // bug), a routine error (defined by the RFC), and supplementary information,
    751 // all bitwise-or'ed together in different regions of the 32 bit return value.
    752 // This means a simple switch on the return codes is not sufficient.
    753 
    754 int MapImportNameStatusToError(OM_uint32 major_status) {
    755   VLOG(1) << "import_name returned 0x" << std::hex << major_status;
    756   if (major_status == GSS_S_COMPLETE)
    757     return OK;
    758   if (GSS_CALLING_ERROR(major_status) != 0)
    759     return ERR_UNEXPECTED;
    760   OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
    761   switch (routine_error) {
    762     case GSS_S_FAILURE:
    763       // Looking at the MIT Kerberos implementation, this typically is returned
    764       // when memory allocation fails. However, the API does not guarantee
    765       // that this is the case, so using ERR_UNEXPECTED rather than
    766       // ERR_OUT_OF_MEMORY.
    767       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    768     case GSS_S_BAD_NAME:
    769     case GSS_S_BAD_NAMETYPE:
    770       return ERR_MALFORMED_IDENTITY;
    771     case GSS_S_DEFECTIVE_TOKEN:
    772       // Not mentioned in the API, but part of code.
    773       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    774     case GSS_S_BAD_MECH:
    775       return ERR_UNSUPPORTED_AUTH_SCHEME;
    776     default:
    777       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    778   }
    779 }
    780 
    781 int MapInitSecContextStatusToError(OM_uint32 major_status) {
    782   VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status;
    783   // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
    784   // other code just checks if major_status is equivalent to it to indicate
    785   // that there are no other errors included.
    786   if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
    787     return OK;
    788   if (GSS_CALLING_ERROR(major_status) != 0)
    789     return ERR_UNEXPECTED;
    790   OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
    791   switch (routine_status) {
    792     case GSS_S_DEFECTIVE_TOKEN:
    793       return ERR_INVALID_RESPONSE;
    794     case GSS_S_DEFECTIVE_CREDENTIAL:
    795       // Not expected since this implementation uses the default credential.
    796       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    797     case GSS_S_BAD_SIG:
    798       // Probably won't happen, but it's a bad response.
    799       return ERR_INVALID_RESPONSE;
    800     case GSS_S_NO_CRED:
    801       return ERR_INVALID_AUTH_CREDENTIALS;
    802     case GSS_S_CREDENTIALS_EXPIRED:
    803       return ERR_INVALID_AUTH_CREDENTIALS;
    804     case GSS_S_BAD_BINDINGS:
    805       // This only happens with mutual authentication.
    806       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    807     case GSS_S_NO_CONTEXT:
    808       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    809     case GSS_S_BAD_NAMETYPE:
    810       return ERR_UNSUPPORTED_AUTH_SCHEME;
    811     case GSS_S_BAD_NAME:
    812       return ERR_UNSUPPORTED_AUTH_SCHEME;
    813     case GSS_S_BAD_MECH:
    814       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
    815     case GSS_S_FAILURE:
    816       // This should be an "Unexpected Security Status" according to the
    817       // GSSAPI documentation, but it's typically used to indicate that
    818       // credentials are not correctly set up on a user machine, such
    819       // as a missing credential cache or hitting this after calling
    820       // kdestroy.
    821       // TODO(cbentzel): Use minor code for even better mapping?
    822       return ERR_MISSING_AUTH_CREDENTIALS;
    823     default:
    824       if (routine_status != 0)
    825         return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    826       break;
    827   }
    828   OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
    829   // Replays could indicate an attack.
    830   if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
    831                              GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
    832     return ERR_INVALID_RESPONSE;
    833 
    834   // At this point, every documented status has been checked.
    835   return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
    836 }
    837 
    838 }
    839 
    840 int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn,
    841                                          gss_buffer_t in_token,
    842                                          gss_buffer_t out_token) {
    843   // Create a name for the principal
    844   // TODO(cbentzel): Just do this on the first pass?
    845   std::string spn_principal = WideToASCII(spn);
    846   gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
    847   spn_buffer.value = const_cast<char*>(spn_principal.c_str());
    848   spn_buffer.length = spn_principal.size() + 1;
    849   OM_uint32 minor_status = 0;
    850   gss_name_t principal_name = GSS_C_NO_NAME;
    851   OM_uint32 major_status = library_->import_name(
    852       &minor_status,
    853       &spn_buffer,
    854       GSS_C_NT_HOSTBASED_SERVICE,
    855       &principal_name);
    856   int rv = MapImportNameStatusToError(major_status);
    857   if (rv != OK) {
    858     LOG(ERROR) << "Problem importing name from "
    859                << "spn \"" << spn_principal << "\"\n"
    860                << DisplayExtendedStatus(library_, major_status, minor_status);
    861     return rv;
    862   }
    863   ScopedName scoped_name(principal_name, library_);
    864 
    865   // Continue creating a security context.
    866   OM_uint32 req_flags = 0;
    867   if (can_delegate_)
    868     req_flags |= GSS_C_DELEG_FLAG;
    869   major_status = library_->init_sec_context(
    870       &minor_status,
    871       GSS_C_NO_CREDENTIAL,
    872       scoped_sec_context_.receive(),
    873       principal_name,
    874       gss_oid_,
    875       req_flags,
    876       GSS_C_INDEFINITE,
    877       GSS_C_NO_CHANNEL_BINDINGS,
    878       in_token,
    879       NULL,  // actual_mech_type
    880       out_token,
    881       NULL,  // ret flags
    882       NULL);
    883   rv = MapInitSecContextStatusToError(major_status);
    884   if (rv != OK) {
    885     LOG(ERROR) << "Problem initializing context. \n"
    886                << DisplayExtendedStatus(library_, major_status, minor_status)
    887                << '\n'
    888                << DescribeContext(library_, scoped_sec_context_.get());
    889   }
    890   return rv;
    891 }
    892 
    893 }  // namespace net
    894