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 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 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 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: 154 ScopedName(gss_name_t name, 155 GSSAPILibrary* gssapi_lib) 156 : name_(name), 157 gssapi_lib_(gssapi_lib) { 158 DCHECK(gssapi_lib_); 159 } 160 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: 184 ScopedBuffer(gss_buffer_t buffer, 185 GSSAPILibrary* gssapi_lib) 186 : buffer_(buffer), 187 gssapi_lib_(gssapi_lib) { 188 DCHECK(gssapi_lib_); 189 } 190 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 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 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 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 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 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 403 GSSAPISharedLibrary::~GSSAPISharedLibrary() { 404 if (gssapi_library_) { 405 base::UnloadNativeLibrary(gssapi_library_); 406 gssapi_library_ = NULL; 407 } 408 } 409 410 bool GSSAPISharedLibrary::Init() { 411 if (!initialized_) 412 InitImpl(); 413 return initialized_; 414 } 415 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 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 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 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 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 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 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 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 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 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 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 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 633 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib) 634 : security_context_(GSS_C_NO_CONTEXT), 635 gssapi_lib_(gssapi_lib) { 636 DCHECK(gssapi_lib_); 637 } 638 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 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 664 HttpAuthGSSAPI::~HttpAuthGSSAPI() { 665 } 666 667 bool HttpAuthGSSAPI::Init() { 668 if (!library_) 669 return false; 670 return library_->Init(); 671 } 672 673 bool HttpAuthGSSAPI::NeedsIdentity() const { 674 return decoded_server_auth_token_.empty(); 675 } 676 677 void HttpAuthGSSAPI::Delegate() { 678 can_delegate_ = true; 679 } 680 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 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 751 int MapImportNameStatusToError(OM_uint32 major_status) { 752 VLOG(1) << "import_name returned 0x" << std::hex << major_status; 753 if (major_status == GSS_S_COMPLETE) 754 return OK; 755 if (GSS_CALLING_ERROR(major_status) != 0) 756 return ERR_UNEXPECTED; 757 OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status); 758 switch (routine_error) { 759 case GSS_S_FAILURE: 760 // Looking at the MIT Kerberos implementation, this typically is returned 761 // when memory allocation fails. However, the API does not guarantee 762 // that this is the case, so using ERR_UNEXPECTED rather than 763 // ERR_OUT_OF_MEMORY. 764 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 765 case GSS_S_BAD_NAME: 766 case GSS_S_BAD_NAMETYPE: 767 return ERR_MALFORMED_IDENTITY; 768 case GSS_S_DEFECTIVE_TOKEN: 769 // Not mentioned in the API, but part of code. 770 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 771 case GSS_S_BAD_MECH: 772 return ERR_UNSUPPORTED_AUTH_SCHEME; 773 default: 774 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 775 } 776 } 777 778 int MapInitSecContextStatusToError(OM_uint32 major_status) { 779 VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status; 780 // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like 781 // other code just checks if major_status is equivalent to it to indicate 782 // that there are no other errors included. 783 if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED) 784 return OK; 785 if (GSS_CALLING_ERROR(major_status) != 0) 786 return ERR_UNEXPECTED; 787 OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status); 788 switch (routine_status) { 789 case GSS_S_DEFECTIVE_TOKEN: 790 return ERR_INVALID_RESPONSE; 791 case GSS_S_DEFECTIVE_CREDENTIAL: 792 // Not expected since this implementation uses the default credential. 793 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 794 case GSS_S_BAD_SIG: 795 // Probably won't happen, but it's a bad response. 796 return ERR_INVALID_RESPONSE; 797 case GSS_S_NO_CRED: 798 return ERR_INVALID_AUTH_CREDENTIALS; 799 case GSS_S_CREDENTIALS_EXPIRED: 800 return ERR_INVALID_AUTH_CREDENTIALS; 801 case GSS_S_BAD_BINDINGS: 802 // This only happens with mutual authentication. 803 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 804 case GSS_S_NO_CONTEXT: 805 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 806 case GSS_S_BAD_NAMETYPE: 807 return ERR_UNSUPPORTED_AUTH_SCHEME; 808 case GSS_S_BAD_NAME: 809 return ERR_UNSUPPORTED_AUTH_SCHEME; 810 case GSS_S_BAD_MECH: 811 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 812 case GSS_S_FAILURE: 813 // This should be an "Unexpected Security Status" according to the 814 // GSSAPI documentation, but it's typically used to indicate that 815 // credentials are not correctly set up on a user machine, such 816 // as a missing credential cache or hitting this after calling 817 // kdestroy. 818 // TODO(cbentzel): Use minor code for even better mapping? 819 return ERR_MISSING_AUTH_CREDENTIALS; 820 default: 821 if (routine_status != 0) 822 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 823 break; 824 } 825 OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status); 826 // Replays could indicate an attack. 827 if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN | 828 GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN)) 829 return ERR_INVALID_RESPONSE; 830 831 // At this point, every documented status has been checked. 832 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 833 } 834 835 } 836 837 int HttpAuthGSSAPI::GetNextSecurityToken(const std::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