• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DisplayStatus(OM_uint32 major_status,OM_uint32 minor_status)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 
DisplayCode(GSSAPILibrary * gssapi_lib,OM_uint32 status,OM_uint32 status_code_type)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 
DisplayExtendedStatus(GSSAPILibrary * gssapi_lib,OM_uint32 major_status,OM_uint32 minor_status)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:
ScopedName(gss_name_t name,GSSAPILibrary * gssapi_lib)152   ScopedName(gss_name_t name,
153              GSSAPILibrary* gssapi_lib)
154       : name_(name),
155         gssapi_lib_(gssapi_lib) {
156     DCHECK(gssapi_lib_);
157   }
158 
~ScopedName()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:
ScopedBuffer(gss_buffer_t buffer,GSSAPILibrary * gssapi_lib)182   ScopedBuffer(gss_buffer_t buffer,
183                GSSAPILibrary* gssapi_lib)
184       : buffer_(buffer),
185         gssapi_lib_(gssapi_lib) {
186     DCHECK(gssapi_lib_);
187   }
188 
~ScopedBuffer()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 
AppendIfPredefinedValue(gss_OID oid,gss_OID predefined_oid,const char * predefined_oid_name)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 
DescribeOid(GSSAPILibrary * gssapi_lib,const gss_OID oid)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 
DescribeName(GSSAPILibrary * gssapi_lib,const gss_name_t name)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 
DescribeContext(GSSAPILibrary * gssapi_lib,const gss_ctx_id_t context_handle)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 
GSSAPISharedLibrary(const std::string & gssapi_library_name)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 
~GSSAPISharedLibrary()398 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
399   if (gssapi_library_) {
400     base::UnloadNativeLibrary(gssapi_library_);
401     gssapi_library_ = NULL;
402   }
403 }
404 
Init()405 bool GSSAPISharedLibrary::Init() {
406   if (!initialized_)
407     InitImpl();
408   return initialized_;
409 }
410 
InitImpl()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 
LoadSharedLibrary()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 
BindMethods(base::NativeLibrary lib)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 
import_name(OM_uint32 * minor_status,const gss_buffer_t input_name_buffer,const gss_OID input_name_type,gss_name_t * output_name)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 
release_name(OM_uint32 * minor_status,gss_name_t * input_name)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 
release_buffer(OM_uint32 * minor_status,gss_buffer_t buffer)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 
display_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)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 
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)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 
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)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 
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)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 
delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)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 
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)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 
ScopedSecurityContext(GSSAPILibrary * gssapi_lib)635 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
636     : security_context_(GSS_C_NO_CONTEXT),
637       gssapi_lib_(gssapi_lib) {
638   DCHECK(gssapi_lib_);
639 }
640 
~ScopedSecurityContext()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 
HttpAuthGSSAPI(GSSAPILibrary * library,const std::string & scheme,gss_OID gss_oid)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 
~HttpAuthGSSAPI()666 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
667 }
668 
Init()669 bool HttpAuthGSSAPI::Init() {
670   if (!library_)
671     return false;
672   return library_->Init();
673 }
674 
NeedsIdentity() const675 bool HttpAuthGSSAPI::NeedsIdentity() const {
676   return decoded_server_auth_token_.empty();
677 }
678 
AllowsExplicitCredentials() const679 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
680   return false;
681 }
682 
Delegate()683 void HttpAuthGSSAPI::Delegate() {
684   can_delegate_ = true;
685 }
686 
ParseChallenge(HttpAuthChallengeTokenizer * tok)687 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
688     HttpAuthChallengeTokenizer* tok) {
689   // Verify the challenge's auth-scheme.
690   if (!LowerCaseEqualsASCII(tok->scheme(),
691                             base::StringToLowerASCII(scheme_).c_str()))
692     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
693 
694   std::string encoded_auth_token = tok->base64_param();
695 
696   if (encoded_auth_token.empty()) {
697     // If a context has already been established, an empty Negotiate challenge
698     // should be treated as a rejection of the current attempt.
699     if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT)
700       return HttpAuth::AUTHORIZATION_RESULT_REJECT;
701     DCHECK(decoded_server_auth_token_.empty());
702     return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
703   } else {
704     // If a context has not already been established, additional tokens should
705     // not be present in the auth challenge.
706     if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT)
707       return HttpAuth::AUTHORIZATION_RESULT_INVALID;
708   }
709 
710   // Make sure the additional token is base64 encoded.
711   std::string decoded_auth_token;
712   bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
713   if (!base64_rv)
714     return HttpAuth::AUTHORIZATION_RESULT_INVALID;
715   decoded_server_auth_token_ = decoded_auth_token;
716   return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
717 }
718 
GenerateAuthToken(const AuthCredentials * credentials,const std::string & spn,std::string * auth_token)719 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
720                                       const std::string& spn,
721                                       std::string* auth_token) {
722   DCHECK(auth_token);
723 
724   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
725   input_token.length = decoded_server_auth_token_.length();
726   input_token.value = (input_token.length > 0) ?
727       const_cast<char*>(decoded_server_auth_token_.data()) :
728       NULL;
729   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
730   ScopedBuffer scoped_output_token(&output_token, library_);
731   int rv = GetNextSecurityToken(spn, &input_token, &output_token);
732   if (rv != OK)
733     return rv;
734 
735   // Base64 encode data in output buffer and prepend the scheme.
736   std::string encode_input(static_cast<char*>(output_token.value),
737                            output_token.length);
738   std::string encode_output;
739   base::Base64Encode(encode_input, &encode_output);
740   *auth_token = scheme_ + " " + encode_output;
741   return OK;
742 }
743 
744 
745 namespace {
746 
747 // GSSAPI status codes consist of a calling error (essentially, a programmer
748 // bug), a routine error (defined by the RFC), and supplementary information,
749 // all bitwise-or'ed together in different regions of the 32 bit return value.
750 // This means a simple switch on the return codes is not sufficient.
751 
MapImportNameStatusToError(OM_uint32 major_status)752 int MapImportNameStatusToError(OM_uint32 major_status) {
753   VLOG(1) << "import_name returned 0x" << std::hex << major_status;
754   if (major_status == GSS_S_COMPLETE)
755     return OK;
756   if (GSS_CALLING_ERROR(major_status) != 0)
757     return ERR_UNEXPECTED;
758   OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
759   switch (routine_error) {
760     case GSS_S_FAILURE:
761       // Looking at the MIT Kerberos implementation, this typically is returned
762       // when memory allocation fails. However, the API does not guarantee
763       // that this is the case, so using ERR_UNEXPECTED rather than
764       // ERR_OUT_OF_MEMORY.
765       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
766     case GSS_S_BAD_NAME:
767     case GSS_S_BAD_NAMETYPE:
768       return ERR_MALFORMED_IDENTITY;
769     case GSS_S_DEFECTIVE_TOKEN:
770       // Not mentioned in the API, but part of code.
771       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
772     case GSS_S_BAD_MECH:
773       return ERR_UNSUPPORTED_AUTH_SCHEME;
774     default:
775       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
776   }
777 }
778 
MapInitSecContextStatusToError(OM_uint32 major_status)779 int MapInitSecContextStatusToError(OM_uint32 major_status) {
780   VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status;
781   // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
782   // other code just checks if major_status is equivalent to it to indicate
783   // that there are no other errors included.
784   if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
785     return OK;
786   if (GSS_CALLING_ERROR(major_status) != 0)
787     return ERR_UNEXPECTED;
788   OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
789   switch (routine_status) {
790     case GSS_S_DEFECTIVE_TOKEN:
791       return ERR_INVALID_RESPONSE;
792     case GSS_S_DEFECTIVE_CREDENTIAL:
793       // Not expected since this implementation uses the default credential.
794       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
795     case GSS_S_BAD_SIG:
796       // Probably won't happen, but it's a bad response.
797       return ERR_INVALID_RESPONSE;
798     case GSS_S_NO_CRED:
799       return ERR_INVALID_AUTH_CREDENTIALS;
800     case GSS_S_CREDENTIALS_EXPIRED:
801       return ERR_INVALID_AUTH_CREDENTIALS;
802     case GSS_S_BAD_BINDINGS:
803       // This only happens with mutual authentication.
804       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
805     case GSS_S_NO_CONTEXT:
806       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
807     case GSS_S_BAD_NAMETYPE:
808       return ERR_UNSUPPORTED_AUTH_SCHEME;
809     case GSS_S_BAD_NAME:
810       return ERR_UNSUPPORTED_AUTH_SCHEME;
811     case GSS_S_BAD_MECH:
812       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
813     case GSS_S_FAILURE:
814       // This should be an "Unexpected Security Status" according to the
815       // GSSAPI documentation, but it's typically used to indicate that
816       // credentials are not correctly set up on a user machine, such
817       // as a missing credential cache or hitting this after calling
818       // kdestroy.
819       // TODO(cbentzel): Use minor code for even better mapping?
820       return ERR_MISSING_AUTH_CREDENTIALS;
821     default:
822       if (routine_status != 0)
823         return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
824       break;
825   }
826   OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
827   // Replays could indicate an attack.
828   if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
829                              GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
830     return ERR_INVALID_RESPONSE;
831 
832   // At this point, every documented status has been checked.
833   return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
834 }
835 
836 }
837 
GetNextSecurityToken(const std::string & spn,gss_buffer_t in_token,gss_buffer_t out_token)838 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
839                                          gss_buffer_t in_token,
840                                          gss_buffer_t out_token) {
841   // Create a name for the principal
842   // TODO(cbentzel): Just do this on the first pass?
843   std::string spn_principal = spn;
844   gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
845   spn_buffer.value = const_cast<char*>(spn_principal.c_str());
846   spn_buffer.length = spn_principal.size() + 1;
847   OM_uint32 minor_status = 0;
848   gss_name_t principal_name = GSS_C_NO_NAME;
849   OM_uint32 major_status = library_->import_name(
850       &minor_status,
851       &spn_buffer,
852       GSS_C_NT_HOSTBASED_SERVICE,
853       &principal_name);
854   int rv = MapImportNameStatusToError(major_status);
855   if (rv != OK) {
856     LOG(ERROR) << "Problem importing name from "
857                << "spn \"" << spn_principal << "\"\n"
858                << DisplayExtendedStatus(library_, major_status, minor_status);
859     return rv;
860   }
861   ScopedName scoped_name(principal_name, library_);
862 
863   // Continue creating a security context.
864   OM_uint32 req_flags = 0;
865   if (can_delegate_)
866     req_flags |= GSS_C_DELEG_FLAG;
867   major_status = library_->init_sec_context(
868       &minor_status,
869       GSS_C_NO_CREDENTIAL,
870       scoped_sec_context_.receive(),
871       principal_name,
872       gss_oid_,
873       req_flags,
874       GSS_C_INDEFINITE,
875       GSS_C_NO_CHANNEL_BINDINGS,
876       in_token,
877       NULL,  // actual_mech_type
878       out_token,
879       NULL,  // ret flags
880       NULL);
881   rv = MapInitSecContextStatusToError(major_status);
882   if (rv != OK) {
883     LOG(ERROR) << "Problem initializing context. \n"
884                << DisplayExtendedStatus(library_, major_status, minor_status)
885                << '\n'
886                << DescribeContext(library_, scoped_sec_context_.get());
887   }
888   return rv;
889 }
890 
891 }  // namespace net
892