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 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 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 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: 152 ScopedName(gss_name_t name, 153 GSSAPILibrary* gssapi_lib) 154 : name_(name), 155 gssapi_lib_(gssapi_lib) { 156 DCHECK(gssapi_lib_); 157 } 158 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: 182 ScopedBuffer(gss_buffer_t buffer, 183 GSSAPILibrary* gssapi_lib) 184 : buffer_(buffer), 185 gssapi_lib_(gssapi_lib) { 186 DCHECK(gssapi_lib_); 187 } 188 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 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 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 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 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 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 398 GSSAPISharedLibrary::~GSSAPISharedLibrary() { 399 if (gssapi_library_) { 400 base::UnloadNativeLibrary(gssapi_library_); 401 gssapi_library_ = NULL; 402 } 403 } 404 405 bool GSSAPISharedLibrary::Init() { 406 if (!initialized_) 407 InitImpl(); 408 return initialized_; 409 } 410 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 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 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 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 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 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 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 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 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 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 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 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 635 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib) 636 : security_context_(GSS_C_NO_CONTEXT), 637 gssapi_lib_(gssapi_lib) { 638 DCHECK(gssapi_lib_); 639 } 640 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 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 666 HttpAuthGSSAPI::~HttpAuthGSSAPI() { 667 } 668 669 bool HttpAuthGSSAPI::Init() { 670 if (!library_) 671 return false; 672 return library_->Init(); 673 } 674 675 bool HttpAuthGSSAPI::NeedsIdentity() const { 676 return decoded_server_auth_token_.empty(); 677 } 678 679 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const { 680 return false; 681 } 682 683 void HttpAuthGSSAPI::Delegate() { 684 can_delegate_ = true; 685 } 686 687 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge( 688 HttpAuthChallengeTokenizer* tok) { 689 // Verify the challenge's auth-scheme. 690 if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str())) 691 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 692 693 std::string encoded_auth_token = tok->base64_param(); 694 695 if (encoded_auth_token.empty()) { 696 // If a context has already been established, an empty Negotiate challenge 697 // should be treated as a rejection of the current attempt. 698 if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT) 699 return HttpAuth::AUTHORIZATION_RESULT_REJECT; 700 DCHECK(decoded_server_auth_token_.empty()); 701 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 702 } else { 703 // If a context has not already been established, additional tokens should 704 // not be present in the auth challenge. 705 if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) 706 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 707 } 708 709 // Make sure the additional token is base64 encoded. 710 std::string decoded_auth_token; 711 bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token); 712 if (!base64_rv) 713 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 714 decoded_server_auth_token_ = decoded_auth_token; 715 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 716 } 717 718 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials, 719 const std::string& spn, 720 std::string* auth_token) { 721 DCHECK(auth_token); 722 723 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 724 input_token.length = decoded_server_auth_token_.length(); 725 input_token.value = (input_token.length > 0) ? 726 const_cast<char*>(decoded_server_auth_token_.data()) : 727 NULL; 728 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 729 ScopedBuffer scoped_output_token(&output_token, library_); 730 int rv = GetNextSecurityToken(spn, &input_token, &output_token); 731 if (rv != OK) 732 return rv; 733 734 // Base64 encode data in output buffer and prepend the scheme. 735 std::string encode_input(static_cast<char*>(output_token.value), 736 output_token.length); 737 std::string encode_output; 738 base::Base64Encode(encode_input, &encode_output); 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::string& 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 = 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 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