• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/file_path.h"
12 #include "base/format_macros.h"
13 #include "base/logging.h"
14 #include "base/string_util.h"
15 #include "base/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 namespace {
26 
27 static gss_OID_desc GSS_C_NT_USER_NAME_VAL = {
28   10,
29   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
30 };
31 static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL = {
32   10,
33   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
34 };
35 static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL = {
36   10,
37   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
38 };
39 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
40   6,
41   const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
42 };
43 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL = {
44   10,
45   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
46 };
47 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = {
48   6,
49   const_cast<char*>("\x2b\x06\01\x05\x06\x03")
50 };
51 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = {
52   6,
53   const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
54 };
55 
56 }  // namespace
57 
58 gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_VAL;
59 gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_VAL;
60 gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_VAL;
61 gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL;
62 gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL;
63 gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_VAL;
64 gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_VAL;
65 
66 namespace net {
67 
68 // These are encoded using ASN.1 BER encoding.
69 
70 // This one is used by Firefox's nsAuthGSSAPI class.
71 gss_OID_desc CHROME_GSS_KRB5_MECH_OID_DESC_VAL = {
72   9,
73   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
74 };
75 
76 gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
77   6,
78   const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
79 };
80 
81 gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL = {
82   10,
83   const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
84 };
85 
86 gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE_X =
87     &CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL;
88 gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE =
89     &CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL;
90 gss_OID CHROME_GSS_KRB5_MECH_OID_DESC =
91     &CHROME_GSS_KRB5_MECH_OID_DESC_VAL;
92 
93 // Debugging helpers.
94 namespace {
95 
DisplayStatus(OM_uint32 major_status,OM_uint32 minor_status)96 std::string DisplayStatus(OM_uint32 major_status,
97                           OM_uint32 minor_status) {
98   if (major_status == GSS_S_COMPLETE)
99     return "OK";
100   return base::StringPrintf("0x%08X 0x%08X", major_status, minor_status);
101 }
102 
DisplayCode(GSSAPILibrary * gssapi_lib,OM_uint32 status,OM_uint32 status_code_type)103 std::string DisplayCode(GSSAPILibrary* gssapi_lib,
104                         OM_uint32 status,
105                         OM_uint32 status_code_type) {
106   const int kMaxDisplayIterations = 8;
107   const size_t kMaxMsgLength = 4096;
108   // msg_ctx needs to be outside the loop because it is invoked multiple times.
109   OM_uint32 msg_ctx = 0;
110   std::string rv = base::StringPrintf("(0x%08X)", status);
111 
112   // This loop should continue iterating until msg_ctx is 0 after the first
113   // iteration. To be cautious and prevent an infinite loop, it stops after
114   // a finite number of iterations as well. As an added sanity check, no
115   // individual message may exceed |kMaxMsgLength|, and the final result
116   // will not exceed |kMaxMsgLength|*2-1.
117   for (int i = 0; i < kMaxDisplayIterations && rv.size() < kMaxMsgLength;
118        ++i) {
119     OM_uint32 min_stat;
120     gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
121     OM_uint32 maj_stat =
122         gssapi_lib->display_status(&min_stat, status, status_code_type,
123                                    GSS_C_NULL_OID, &msg_ctx, &msg);
124     if (maj_stat == GSS_S_COMPLETE) {
125       int msg_len = (msg.length > kMaxMsgLength) ?
126           static_cast<int>(kMaxMsgLength) :
127           static_cast<int>(msg.length);
128       if (msg_len > 0 && msg.value != NULL) {
129         rv += base::StringPrintf(" %.*s", msg_len,
130                                  static_cast<char*>(msg.value));
131       }
132     }
133     gssapi_lib->release_buffer(&min_stat, &msg);
134     if (!msg_ctx)
135       break;
136   }
137   return rv;
138 }
139 
DisplayExtendedStatus(GSSAPILibrary * gssapi_lib,OM_uint32 major_status,OM_uint32 minor_status)140 std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
141                                   OM_uint32 major_status,
142                                   OM_uint32 minor_status) {
143   if (major_status == GSS_S_COMPLETE)
144     return "OK";
145   std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
146   std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
147   return base::StringPrintf("Major: %s | Minor: %s", major.c_str(),
148                             minor.c_str());
149 }
150 
151 // ScopedName releases a gss_name_t when it goes out of scope.
152 class ScopedName {
153  public:
ScopedName(gss_name_t name,GSSAPILibrary * gssapi_lib)154   ScopedName(gss_name_t name,
155              GSSAPILibrary* gssapi_lib)
156       : name_(name),
157         gssapi_lib_(gssapi_lib) {
158     DCHECK(gssapi_lib_);
159   }
160 
~ScopedName()161   ~ScopedName() {
162     if (name_ != GSS_C_NO_NAME) {
163       OM_uint32 minor_status = 0;
164       OM_uint32 major_status =
165           gssapi_lib_->release_name(&minor_status, &name_);
166       if (major_status != GSS_S_COMPLETE) {
167         LOG(WARNING) << "Problem releasing name. "
168                      << DisplayStatus(major_status, minor_status);
169       }
170       name_ = GSS_C_NO_NAME;
171     }
172   }
173 
174  private:
175   gss_name_t name_;
176   GSSAPILibrary* gssapi_lib_;
177 
178   DISALLOW_COPY_AND_ASSIGN(ScopedName);
179 };
180 
181 // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
182 class ScopedBuffer {
183  public:
ScopedBuffer(gss_buffer_t buffer,GSSAPILibrary * gssapi_lib)184   ScopedBuffer(gss_buffer_t buffer,
185                GSSAPILibrary* gssapi_lib)
186       : buffer_(buffer),
187         gssapi_lib_(gssapi_lib) {
188     DCHECK(gssapi_lib_);
189   }
190 
~ScopedBuffer()191   ~ScopedBuffer() {
192     if (buffer_ != GSS_C_NO_BUFFER) {
193       OM_uint32 minor_status = 0;
194       OM_uint32 major_status =
195           gssapi_lib_->release_buffer(&minor_status, buffer_);
196       if (major_status != GSS_S_COMPLETE) {
197         LOG(WARNING) << "Problem releasing buffer. "
198                      << DisplayStatus(major_status, minor_status);
199       }
200       buffer_ = GSS_C_NO_BUFFER;
201     }
202   }
203 
204  private:
205   gss_buffer_t buffer_;
206   GSSAPILibrary* gssapi_lib_;
207 
208   DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
209 };
210 
211 namespace {
212 
AppendIfPredefinedValue(gss_OID oid,gss_OID predefined_oid,const char * predefined_oid_name)213 std::string AppendIfPredefinedValue(gss_OID oid,
214                                     gss_OID predefined_oid,
215                                     const char* predefined_oid_name) {
216   DCHECK(oid);
217   DCHECK(predefined_oid);
218   DCHECK(predefined_oid_name);
219   std::string output;
220   if (oid->length != predefined_oid->length)
221     return output;
222   if (0 != memcmp(oid->elements,
223                   predefined_oid->elements,
224                   predefined_oid->length))
225     return output;
226 
227   output += " (";
228   output += predefined_oid_name;
229   output += ")";
230   return output;
231 }
232 
233 }  // namespace
234 
DescribeOid(GSSAPILibrary * gssapi_lib,const gss_OID oid)235 std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) {
236   if (!oid)
237     return "<NULL>";
238   std::string output;
239   const size_t kMaxCharsToPrint = 1024;
240   OM_uint32 byte_length = oid->length;
241   size_t char_length = byte_length / sizeof(char);
242   if (char_length > kMaxCharsToPrint) {
243     // This might be a plain ASCII string.
244     // Check if the first |kMaxCharsToPrint| characters
245     // contain only printable characters and are NULL terminated.
246     const char* str = reinterpret_cast<const char*>(oid);
247     bool is_printable = true;
248     size_t str_length = 0;
249     for ( ; str_length < kMaxCharsToPrint; ++str_length) {
250       if (!str[str_length])
251         break;
252       if (!isprint(str[str_length])) {
253         is_printable = false;
254         break;
255       }
256     }
257     if (!str[str_length]) {
258       output += base::StringPrintf("\"%s\"", str);
259       return output;
260     }
261   }
262   output = base::StringPrintf("(%u) \"", byte_length);
263   if (!oid->elements) {
264     output += "<NULL>";
265     return output;
266   }
267   const unsigned char* elements =
268       reinterpret_cast<const unsigned char*>(oid->elements);
269   // Don't print more than |kMaxCharsToPrint| characters.
270   size_t i = 0;
271   for ( ; (i < byte_length) && (i < kMaxCharsToPrint); ++i) {
272     output += base::StringPrintf("\\x%02X", elements[i]);
273   }
274   if (i >= kMaxCharsToPrint)
275     output += "...";
276   output += "\"";
277 
278   // Check if the OID is one of the predefined values.
279   output += AppendIfPredefinedValue(oid,
280                                     GSS_C_NT_USER_NAME,
281                                     "GSS_C_NT_USER_NAME");
282   output += AppendIfPredefinedValue(oid,
283                                     GSS_C_NT_MACHINE_UID_NAME,
284                                     "GSS_C_NT_MACHINE_UID_NAME");
285   output += AppendIfPredefinedValue(oid,
286                                     GSS_C_NT_STRING_UID_NAME,
287                                     "GSS_C_NT_STRING_UID_NAME");
288   output += AppendIfPredefinedValue(oid,
289                                     GSS_C_NT_HOSTBASED_SERVICE_X,
290                                     "GSS_C_NT_HOSTBASED_SERVICE_X");
291   output += AppendIfPredefinedValue(oid,
292                                     GSS_C_NT_HOSTBASED_SERVICE,
293                                     "GSS_C_NT_HOSTBASED_SERVICE");
294   output += AppendIfPredefinedValue(oid,
295                                     GSS_C_NT_ANONYMOUS,
296                                     "GSS_C_NT_ANONYMOUS");
297   output += AppendIfPredefinedValue(oid,
298                                     GSS_C_NT_EXPORT_NAME,
299                                     "GSS_C_NT_EXPORT_NAME");
300 
301   return output;
302 }
303 
DescribeName(GSSAPILibrary * gssapi_lib,const gss_name_t name)304 std::string DescribeName(GSSAPILibrary* gssapi_lib, const gss_name_t name) {
305   OM_uint32 major_status = 0;
306   OM_uint32 minor_status = 0;
307   gss_buffer_desc_struct output_name_buffer = GSS_C_EMPTY_BUFFER;
308   gss_OID_desc output_name_type_desc = GSS_C_EMPTY_BUFFER;
309   gss_OID output_name_type = &output_name_type_desc;
310   major_status = gssapi_lib->display_name(&minor_status,
311                                           name,
312                                           &output_name_buffer,
313                                           &output_name_type);
314   ScopedBuffer scoped_output_name(&output_name_buffer, gssapi_lib);
315   if (major_status != GSS_S_COMPLETE) {
316     std::string error =
317         base::StringPrintf("Unable to describe name 0x%p, %s",
318                            name,
319                            DisplayExtendedStatus(gssapi_lib,
320                                                  major_status,
321                                                  minor_status).c_str());
322     return error;
323   }
324   int len = output_name_buffer.length;
325   std::string description = base::StringPrintf(
326       "%*s (Type %s)",
327       len,
328       reinterpret_cast<const char*>(output_name_buffer.value),
329       DescribeOid(gssapi_lib, output_name_type).c_str());
330   return description;
331 }
332 
DescribeContext(GSSAPILibrary * gssapi_lib,const gss_ctx_id_t context_handle)333 std::string DescribeContext(GSSAPILibrary* gssapi_lib,
334                             const gss_ctx_id_t context_handle) {
335   OM_uint32 major_status = 0;
336   OM_uint32 minor_status = 0;
337   gss_name_t src_name = GSS_C_NO_NAME;
338   gss_name_t targ_name = GSS_C_NO_NAME;
339   OM_uint32 lifetime_rec = 0;
340   gss_OID mech_type = GSS_C_NO_OID;
341   OM_uint32 ctx_flags = 0;
342   int locally_initiated = 0;
343   int open = 0;
344   major_status = gssapi_lib->inquire_context(&minor_status,
345                                              context_handle,
346                                              &src_name,
347                                              &targ_name,
348                                              &lifetime_rec,
349                                              &mech_type,
350                                              &ctx_flags,
351                                              &locally_initiated,
352                                              &open);
353   ScopedName(src_name, gssapi_lib);
354   ScopedName(targ_name, gssapi_lib);
355   if (major_status != GSS_S_COMPLETE) {
356     std::string error =
357         base::StringPrintf("Unable to describe context 0x%p, %s",
358                            context_handle,
359                            DisplayExtendedStatus(gssapi_lib,
360                                                  major_status,
361                                                  minor_status).c_str());
362     return error;
363   }
364   std::string source(DescribeName(gssapi_lib, src_name));
365   std::string target(DescribeName(gssapi_lib, targ_name));
366   std::string description = base::StringPrintf("Context 0x%p: "
367                                                "Source \"%s\", "
368                                                "Target \"%s\", "
369                                                 "lifetime %d, "
370                                                 "mechanism %s, "
371                                                 "flags 0x%08X, "
372                                                 "local %d, "
373                                                 "open %d",
374                                                 context_handle,
375                                                 source.c_str(),
376                                                 target.c_str(),
377                                                 lifetime_rec,
378                                                 DescribeOid(gssapi_lib,
379                                                             mech_type).c_str(),
380                                                 ctx_flags,
381                                                 locally_initiated,
382                                                 open);
383   return description;
384 }
385 
386 }  // namespace
387 
GSSAPISharedLibrary(const std::string & gssapi_library_name)388 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name)
389     : initialized_(false),
390       gssapi_library_name_(gssapi_library_name),
391       gssapi_library_(NULL),
392       import_name_(NULL),
393       release_name_(NULL),
394       release_buffer_(NULL),
395       display_name_(NULL),
396       display_status_(NULL),
397       init_sec_context_(NULL),
398       wrap_size_limit_(NULL),
399       delete_sec_context_(NULL),
400       inquire_context_(NULL) {
401 }
402 
~GSSAPISharedLibrary()403 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
404   if (gssapi_library_) {
405     base::UnloadNativeLibrary(gssapi_library_);
406     gssapi_library_ = NULL;
407   }
408 }
409 
Init()410 bool GSSAPISharedLibrary::Init() {
411   if (!initialized_)
412     InitImpl();
413   return initialized_;
414 }
415 
InitImpl()416 bool GSSAPISharedLibrary::InitImpl() {
417   DCHECK(!initialized_);
418   gssapi_library_ = LoadSharedLibrary();
419   if (gssapi_library_ == NULL)
420     return false;
421   initialized_ = true;
422   return true;
423 }
424 
LoadSharedLibrary()425 base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() {
426   const char** library_names;
427   size_t num_lib_names;
428   const char* user_specified_library[1];
429   if (!gssapi_library_name_.empty()) {
430     user_specified_library[0] = gssapi_library_name_.c_str();
431     library_names = user_specified_library;
432     num_lib_names = 1;
433   } else {
434     static const char* kDefaultLibraryNames[] = {
435 #if defined(OS_MACOSX)
436       "libgssapi_krb5.dylib"  // MIT Kerberos
437 #else
438       "libgssapi_krb5.so.2",  // MIT Kerberos - FC, Suse10, Debian
439       "libgssapi.so.4",       // Heimdal - Suse10, MDK
440       "libgssapi.so.2",       // Heimdal - Gentoo
441       "libgssapi.so.1"        // Heimdal - Suse9, CITI - FC, MDK, Suse10
442 #endif
443     };
444     library_names = kDefaultLibraryNames;
445     num_lib_names = arraysize(kDefaultLibraryNames);
446   }
447 
448   for (size_t i = 0; i < num_lib_names; ++i) {
449     const char* library_name = library_names[i];
450     FilePath file_path(library_name);
451 
452     // TODO(asanka): Move library loading to a separate thread.
453     //               http://crbug.com/66702
454     base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily;
455     base::NativeLibrary lib = base::LoadNativeLibrary(file_path, NULL);
456     if (lib) {
457       // Only return this library if we can bind the functions we need.
458       if (BindMethods(lib))
459         return lib;
460       base::UnloadNativeLibrary(lib);
461     }
462   }
463   LOG(WARNING) << "Unable to find a compatible GSSAPI library";
464   return NULL;
465 }
466 
467 #define BIND(lib, x)                                                    \
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 
BindMethods(base::NativeLibrary lib)475 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) {
476   DCHECK(lib != NULL);
477 
478   BIND(lib, import_name);
479   BIND(lib, release_name);
480   BIND(lib, release_buffer);
481   BIND(lib, display_name);
482   BIND(lib, display_status);
483   BIND(lib, init_sec_context);
484   BIND(lib, wrap_size_limit);
485   BIND(lib, delete_sec_context);
486   BIND(lib, inquire_context);
487 
488   import_name_ = import_name;
489   release_name_ = release_name;
490   release_buffer_ = release_buffer;
491   display_name_ = display_name;
492   display_status_ = display_status;
493   init_sec_context_ = init_sec_context;
494   wrap_size_limit_ = wrap_size_limit;
495   delete_sec_context_ = delete_sec_context;
496   inquire_context_ = inquire_context;
497 
498   return true;
499 }
500 
501 #undef BIND
502 
import_name(OM_uint32 * minor_status,const gss_buffer_t input_name_buffer,const gss_OID input_name_type,gss_name_t * output_name)503 OM_uint32 GSSAPISharedLibrary::import_name(
504     OM_uint32* minor_status,
505     const gss_buffer_t input_name_buffer,
506     const gss_OID input_name_type,
507     gss_name_t* output_name) {
508   DCHECK(initialized_);
509   return import_name_(minor_status, input_name_buffer, input_name_type,
510                       output_name);
511 }
512 
release_name(OM_uint32 * minor_status,gss_name_t * input_name)513 OM_uint32 GSSAPISharedLibrary::release_name(
514     OM_uint32* minor_status,
515     gss_name_t* input_name) {
516   DCHECK(initialized_);
517   return release_name_(minor_status, input_name);
518 }
519 
release_buffer(OM_uint32 * minor_status,gss_buffer_t buffer)520 OM_uint32 GSSAPISharedLibrary::release_buffer(
521     OM_uint32* minor_status,
522     gss_buffer_t buffer) {
523   DCHECK(initialized_);
524   return release_buffer_(minor_status, buffer);
525 }
526 
display_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)527 OM_uint32 GSSAPISharedLibrary::display_name(
528     OM_uint32* minor_status,
529     const gss_name_t input_name,
530     gss_buffer_t output_name_buffer,
531     gss_OID* output_name_type) {
532   DCHECK(initialized_);
533   return display_name_(minor_status,
534                        input_name,
535                        output_name_buffer,
536                        output_name_type);
537 }
538 
display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,const gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)539 OM_uint32 GSSAPISharedLibrary::display_status(
540     OM_uint32* minor_status,
541     OM_uint32 status_value,
542     int status_type,
543     const gss_OID mech_type,
544     OM_uint32* message_context,
545     gss_buffer_t status_string) {
546   DCHECK(initialized_);
547   return display_status_(minor_status, status_value, status_type, mech_type,
548                          message_context, status_string);
549 }
550 
init_sec_context(OM_uint32 * minor_status,const gss_cred_id_t initiator_cred_handle,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)551 OM_uint32 GSSAPISharedLibrary::init_sec_context(
552     OM_uint32* minor_status,
553     const gss_cred_id_t initiator_cred_handle,
554     gss_ctx_id_t* context_handle,
555     const gss_name_t target_name,
556     const gss_OID mech_type,
557     OM_uint32 req_flags,
558     OM_uint32 time_req,
559     const gss_channel_bindings_t input_chan_bindings,
560     const gss_buffer_t input_token,
561     gss_OID* actual_mech_type,
562     gss_buffer_t output_token,
563     OM_uint32* ret_flags,
564     OM_uint32* time_rec) {
565   DCHECK(initialized_);
566   return init_sec_context_(minor_status,
567                            initiator_cred_handle,
568                            context_handle,
569                            target_name,
570                            mech_type,
571                            req_flags,
572                            time_req,
573                            input_chan_bindings,
574                            input_token,
575                            actual_mech_type,
576                            output_token,
577                            ret_flags,
578                            time_rec);
579 }
580 
wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)581 OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
582     OM_uint32* minor_status,
583     const gss_ctx_id_t context_handle,
584     int conf_req_flag,
585     gss_qop_t qop_req,
586     OM_uint32 req_output_size,
587     OM_uint32* max_input_size) {
588   DCHECK(initialized_);
589   return wrap_size_limit_(minor_status,
590                           context_handle,
591                           conf_req_flag,
592                           qop_req,
593                           req_output_size,
594                           max_input_size);
595 }
596 
delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)597 OM_uint32 GSSAPISharedLibrary::delete_sec_context(
598     OM_uint32* minor_status,
599     gss_ctx_id_t* context_handle,
600     gss_buffer_t output_token) {
601   // This is called from the owner class' destructor, even if
602   // Init() is not called, so we can't assume |initialized_|
603   // is set.
604   if (!initialized_)
605     return 0;
606   return delete_sec_context_(minor_status,
607                              context_handle,
608                              output_token);
609 }
610 
inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * open)611 OM_uint32 GSSAPISharedLibrary::inquire_context(
612     OM_uint32* minor_status,
613     const gss_ctx_id_t context_handle,
614     gss_name_t* src_name,
615     gss_name_t* targ_name,
616     OM_uint32* lifetime_rec,
617     gss_OID* mech_type,
618     OM_uint32* ctx_flags,
619     int* locally_initiated,
620     int* open) {
621   DCHECK(initialized_);
622   return inquire_context_(minor_status,
623                           context_handle,
624                           src_name,
625                           targ_name,
626                           lifetime_rec,
627                           mech_type,
628                           ctx_flags,
629                           locally_initiated,
630                           open);
631 }
632 
ScopedSecurityContext(GSSAPILibrary * gssapi_lib)633 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
634     : security_context_(GSS_C_NO_CONTEXT),
635       gssapi_lib_(gssapi_lib) {
636   DCHECK(gssapi_lib_);
637 }
638 
~ScopedSecurityContext()639 ScopedSecurityContext::~ScopedSecurityContext() {
640   if (security_context_ != GSS_C_NO_CONTEXT) {
641     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
642     OM_uint32 minor_status = 0;
643     OM_uint32 major_status = gssapi_lib_->delete_sec_context(
644         &minor_status, &security_context_, &output_token);
645     if (major_status != GSS_S_COMPLETE) {
646       LOG(WARNING) << "Problem releasing security_context. "
647                    << DisplayStatus(major_status, minor_status);
648     }
649     security_context_ = GSS_C_NO_CONTEXT;
650   }
651 }
652 
HttpAuthGSSAPI(GSSAPILibrary * library,const std::string & scheme,gss_OID gss_oid)653 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
654                                const std::string& scheme,
655                                gss_OID gss_oid)
656     : scheme_(scheme),
657       gss_oid_(gss_oid),
658       library_(library),
659       scoped_sec_context_(library),
660       can_delegate_(false) {
661   DCHECK(library_);
662 }
663 
~HttpAuthGSSAPI()664 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
665 }
666 
Init()667 bool HttpAuthGSSAPI::Init() {
668   if (!library_)
669     return false;
670   return library_->Init();
671 }
672 
NeedsIdentity() const673 bool HttpAuthGSSAPI::NeedsIdentity() const {
674   return decoded_server_auth_token_.empty();
675 }
676 
Delegate()677 void HttpAuthGSSAPI::Delegate() {
678   can_delegate_ = true;
679 }
680 
ParseChallenge(HttpAuth::ChallengeTokenizer * tok)681 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
682     HttpAuth::ChallengeTokenizer* tok) {
683   // Verify the challenge's auth-scheme.
684   if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
685     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
686 
687   std::string encoded_auth_token = tok->base64_param();
688 
689   if (encoded_auth_token.empty()) {
690     // If a context has already been established, an empty Negotiate challenge
691     // should be treated as a rejection of the current attempt.
692     if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT)
693       return HttpAuth::AUTHORIZATION_RESULT_REJECT;
694     DCHECK(decoded_server_auth_token_.empty());
695     return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
696   } else {
697     // If a context has not already been established, additional tokens should
698     // not be present in the auth challenge.
699     if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT)
700       return HttpAuth::AUTHORIZATION_RESULT_INVALID;
701   }
702 
703   // Make sure the additional token is base64 encoded.
704   std::string decoded_auth_token;
705   bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
706   if (!base64_rv)
707     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
708   decoded_server_auth_token_ = decoded_auth_token;
709   return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
710 }
711 
GenerateAuthToken(const string16 * username,const string16 * password,const std::wstring & spn,std::string * auth_token)712 int HttpAuthGSSAPI::GenerateAuthToken(const string16* username,
713                                       const string16* password,
714                                       const std::wstring& spn,
715                                       std::string* auth_token) {
716   DCHECK(auth_token);
717   DCHECK(username == NULL && password == NULL);
718 
719   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
720   input_token.length = decoded_server_auth_token_.length();
721   input_token.value = (input_token.length > 0) ?
722       const_cast<char*>(decoded_server_auth_token_.data()) :
723       NULL;
724   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
725   ScopedBuffer scoped_output_token(&output_token, library_);
726   int rv = GetNextSecurityToken(spn, &input_token, &output_token);
727   if (rv != OK)
728     return rv;
729 
730   // Base64 encode data in output buffer and prepend the scheme.
731   std::string encode_input(static_cast<char*>(output_token.value),
732                            output_token.length);
733   std::string encode_output;
734   bool base64_rv = base::Base64Encode(encode_input, &encode_output);
735   if (!base64_rv) {
736     LOG(ERROR) << "Base64 encoding of auth token failed.";
737     return ERR_ENCODING_CONVERSION_FAILED;
738   }
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 
MapImportNameStatusToError(OM_uint32 major_status)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 
MapInitSecContextStatusToError(OM_uint32 major_status)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 
GetNextSecurityToken(const std::wstring & spn,gss_buffer_t in_token,gss_buffer_t out_token)837 int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& 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 = WideToASCII(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       CHROME_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