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