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 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 // These are encoded using ASN.1 BER encoding. 26 namespace { 27 28 static gss_OID_desc GSS_C_NT_USER_NAME_VAL = { 29 10, 30 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01") 31 }; 32 static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL = { 33 10, 34 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02") 35 }; 36 static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL = { 37 10, 38 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03") 39 }; 40 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = { 41 6, 42 const_cast<char*>("\x2b\x06\x01\x05\x06\x02") 43 }; 44 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL = { 45 10, 46 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04") 47 }; 48 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = { 49 6, 50 const_cast<char*>("\x2b\x06\01\x05\x06\x03") 51 }; 52 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = { 53 6, 54 const_cast<char*>("\x2b\x06\x01\x05\x06\x04") 55 }; 56 57 } // namespace 58 59 // Heimdal >= 1.4 will define the following as preprocessor macros. 60 // To avoid conflicting declarations, we have to undefine these. 61 #undef GSS_C_NT_USER_NAME 62 #undef GSS_C_NT_MACHINE_UID_NAME 63 #undef GSS_C_NT_STRING_UID_NAME 64 #undef GSS_C_NT_HOSTBASED_SERVICE_X 65 #undef GSS_C_NT_HOSTBASED_SERVICE 66 #undef GSS_C_NT_ANONYMOUS 67 #undef GSS_C_NT_EXPORT_NAME 68 69 gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_VAL; 70 gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_VAL; 71 gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_VAL; 72 gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL; 73 gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL; 74 gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_VAL; 75 gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_VAL; 76 77 namespace net { 78 79 // Exported mechanism for GSSAPI. We always use SPNEGO: 80 81 // iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2) 82 gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = { 83 6, 84 const_cast<char*>("\x2b\x06\x01\x05\x05\x02") 85 }; 86 87 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC = 88 &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL; 89 90 // Debugging helpers. 91 namespace { 92 93 std::string DisplayStatus(OM_uint32 major_status, 94 OM_uint32 minor_status) { 95 if (major_status == GSS_S_COMPLETE) 96 return "OK"; 97 return base::StringPrintf("0x%08X 0x%08X", major_status, minor_status); 98 } 99 100 std::string DisplayCode(GSSAPILibrary* gssapi_lib, 101 OM_uint32 status, 102 OM_uint32 status_code_type) { 103 const int kMaxDisplayIterations = 8; 104 const size_t kMaxMsgLength = 4096; 105 // msg_ctx needs to be outside the loop because it is invoked multiple times. 106 OM_uint32 msg_ctx = 0; 107 std::string rv = base::StringPrintf("(0x%08X)", status); 108 109 // This loop should continue iterating until msg_ctx is 0 after the first 110 // iteration. To be cautious and prevent an infinite loop, it stops after 111 // a finite number of iterations as well. As an added sanity check, no 112 // individual message may exceed |kMaxMsgLength|, and the final result 113 // will not exceed |kMaxMsgLength|*2-1. 114 for (int i = 0; i < kMaxDisplayIterations && rv.size() < kMaxMsgLength; 115 ++i) { 116 OM_uint32 min_stat; 117 gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER; 118 OM_uint32 maj_stat = 119 gssapi_lib->display_status(&min_stat, status, status_code_type, 120 GSS_C_NULL_OID, &msg_ctx, &msg); 121 if (maj_stat == GSS_S_COMPLETE) { 122 int msg_len = (msg.length > kMaxMsgLength) ? 123 static_cast<int>(kMaxMsgLength) : 124 static_cast<int>(msg.length); 125 if (msg_len > 0 && msg.value != NULL) { 126 rv += base::StringPrintf(" %.*s", msg_len, 127 static_cast<char*>(msg.value)); 128 } 129 } 130 gssapi_lib->release_buffer(&min_stat, &msg); 131 if (!msg_ctx) 132 break; 133 } 134 return rv; 135 } 136 137 std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib, 138 OM_uint32 major_status, 139 OM_uint32 minor_status) { 140 if (major_status == GSS_S_COMPLETE) 141 return "OK"; 142 std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE); 143 std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE); 144 return base::StringPrintf("Major: %s | Minor: %s", major.c_str(), 145 minor.c_str()); 146 } 147 148 // ScopedName releases a gss_name_t when it goes out of scope. 149 class ScopedName { 150 public: 151 ScopedName(gss_name_t name, 152 GSSAPILibrary* gssapi_lib) 153 : name_(name), 154 gssapi_lib_(gssapi_lib) { 155 DCHECK(gssapi_lib_); 156 } 157 158 ~ScopedName() { 159 if (name_ != GSS_C_NO_NAME) { 160 OM_uint32 minor_status = 0; 161 OM_uint32 major_status = 162 gssapi_lib_->release_name(&minor_status, &name_); 163 if (major_status != GSS_S_COMPLETE) { 164 LOG(WARNING) << "Problem releasing name. " 165 << DisplayStatus(major_status, minor_status); 166 } 167 name_ = GSS_C_NO_NAME; 168 } 169 } 170 171 private: 172 gss_name_t name_; 173 GSSAPILibrary* gssapi_lib_; 174 175 DISALLOW_COPY_AND_ASSIGN(ScopedName); 176 }; 177 178 // ScopedBuffer releases a gss_buffer_t when it goes out of scope. 179 class ScopedBuffer { 180 public: 181 ScopedBuffer(gss_buffer_t buffer, 182 GSSAPILibrary* gssapi_lib) 183 : buffer_(buffer), 184 gssapi_lib_(gssapi_lib) { 185 DCHECK(gssapi_lib_); 186 } 187 188 ~ScopedBuffer() { 189 if (buffer_ != GSS_C_NO_BUFFER) { 190 OM_uint32 minor_status = 0; 191 OM_uint32 major_status = 192 gssapi_lib_->release_buffer(&minor_status, buffer_); 193 if (major_status != GSS_S_COMPLETE) { 194 LOG(WARNING) << "Problem releasing buffer. " 195 << DisplayStatus(major_status, minor_status); 196 } 197 buffer_ = GSS_C_NO_BUFFER; 198 } 199 } 200 201 private: 202 gss_buffer_t buffer_; 203 GSSAPILibrary* gssapi_lib_; 204 205 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer); 206 }; 207 208 namespace { 209 210 std::string AppendIfPredefinedValue(gss_OID oid, 211 gss_OID predefined_oid, 212 const char* predefined_oid_name) { 213 DCHECK(oid); 214 DCHECK(predefined_oid); 215 DCHECK(predefined_oid_name); 216 std::string output; 217 if (oid->length != predefined_oid->length) 218 return output; 219 if (0 != memcmp(oid->elements, 220 predefined_oid->elements, 221 predefined_oid->length)) 222 return output; 223 224 output += " ("; 225 output += predefined_oid_name; 226 output += ")"; 227 return output; 228 } 229 230 } // namespace 231 232 std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) { 233 if (!oid) 234 return "<NULL>"; 235 std::string output; 236 const size_t kMaxCharsToPrint = 1024; 237 OM_uint32 byte_length = oid->length; 238 size_t char_length = byte_length / sizeof(char); 239 if (char_length > kMaxCharsToPrint) { 240 // This might be a plain ASCII string. 241 // Check if the first |kMaxCharsToPrint| characters 242 // contain only printable characters and are NULL terminated. 243 const char* str = reinterpret_cast<const char*>(oid); 244 size_t str_length = 0; 245 for ( ; str_length < kMaxCharsToPrint; ++str_length) { 246 if (!str[str_length] || !isprint(str[str_length])) 247 break; 248 } 249 if (!str[str_length]) { 250 output += base::StringPrintf("\"%s\"", str); 251 return output; 252 } 253 } 254 output = base::StringPrintf("(%u) \"", byte_length); 255 if (!oid->elements) { 256 output += "<NULL>"; 257 return output; 258 } 259 const unsigned char* elements = 260 reinterpret_cast<const unsigned char*>(oid->elements); 261 // Don't print more than |kMaxCharsToPrint| characters. 262 size_t i = 0; 263 for ( ; (i < byte_length) && (i < kMaxCharsToPrint); ++i) { 264 output += base::StringPrintf("\\x%02X", elements[i]); 265 } 266 if (i >= kMaxCharsToPrint) 267 output += "..."; 268 output += "\""; 269 270 // Check if the OID is one of the predefined values. 271 output += AppendIfPredefinedValue(oid, 272 GSS_C_NT_USER_NAME, 273 "GSS_C_NT_USER_NAME"); 274 output += AppendIfPredefinedValue(oid, 275 GSS_C_NT_MACHINE_UID_NAME, 276 "GSS_C_NT_MACHINE_UID_NAME"); 277 output += AppendIfPredefinedValue(oid, 278 GSS_C_NT_STRING_UID_NAME, 279 "GSS_C_NT_STRING_UID_NAME"); 280 output += AppendIfPredefinedValue(oid, 281 GSS_C_NT_HOSTBASED_SERVICE_X, 282 "GSS_C_NT_HOSTBASED_SERVICE_X"); 283 output += AppendIfPredefinedValue(oid, 284 GSS_C_NT_HOSTBASED_SERVICE, 285 "GSS_C_NT_HOSTBASED_SERVICE"); 286 output += AppendIfPredefinedValue(oid, 287 GSS_C_NT_ANONYMOUS, 288 "GSS_C_NT_ANONYMOUS"); 289 output += AppendIfPredefinedValue(oid, 290 GSS_C_NT_EXPORT_NAME, 291 "GSS_C_NT_EXPORT_NAME"); 292 293 return output; 294 } 295 296 std::string DescribeName(GSSAPILibrary* gssapi_lib, const gss_name_t name) { 297 OM_uint32 major_status = 0; 298 OM_uint32 minor_status = 0; 299 gss_buffer_desc_struct output_name_buffer = GSS_C_EMPTY_BUFFER; 300 gss_OID_desc output_name_type_desc = GSS_C_EMPTY_BUFFER; 301 gss_OID output_name_type = &output_name_type_desc; 302 major_status = gssapi_lib->display_name(&minor_status, 303 name, 304 &output_name_buffer, 305 &output_name_type); 306 ScopedBuffer scoped_output_name(&output_name_buffer, gssapi_lib); 307 if (major_status != GSS_S_COMPLETE) { 308 std::string error = 309 base::StringPrintf("Unable to describe name 0x%p, %s", 310 name, 311 DisplayExtendedStatus(gssapi_lib, 312 major_status, 313 minor_status).c_str()); 314 return error; 315 } 316 int len = output_name_buffer.length; 317 std::string description = base::StringPrintf( 318 "%*s (Type %s)", 319 len, 320 reinterpret_cast<const char*>(output_name_buffer.value), 321 DescribeOid(gssapi_lib, output_name_type).c_str()); 322 return description; 323 } 324 325 std::string DescribeContext(GSSAPILibrary* gssapi_lib, 326 const gss_ctx_id_t context_handle) { 327 OM_uint32 major_status = 0; 328 OM_uint32 minor_status = 0; 329 gss_name_t src_name = GSS_C_NO_NAME; 330 gss_name_t targ_name = GSS_C_NO_NAME; 331 OM_uint32 lifetime_rec = 0; 332 gss_OID mech_type = GSS_C_NO_OID; 333 OM_uint32 ctx_flags = 0; 334 int locally_initiated = 0; 335 int open = 0; 336 if (context_handle == GSS_C_NO_CONTEXT) 337 return std::string("Context: GSS_C_NO_CONTEXT"); 338 major_status = gssapi_lib->inquire_context(&minor_status, 339 context_handle, 340 &src_name, 341 &targ_name, 342 &lifetime_rec, 343 &mech_type, 344 &ctx_flags, 345 &locally_initiated, 346 &open); 347 ScopedName(src_name, gssapi_lib); 348 ScopedName(targ_name, gssapi_lib); 349 if (major_status != GSS_S_COMPLETE) { 350 std::string error = 351 base::StringPrintf("Unable to describe context 0x%p, %s", 352 context_handle, 353 DisplayExtendedStatus(gssapi_lib, 354 major_status, 355 minor_status).c_str()); 356 return error; 357 } 358 std::string source(DescribeName(gssapi_lib, src_name)); 359 std::string target(DescribeName(gssapi_lib, targ_name)); 360 std::string description = base::StringPrintf("Context 0x%p: " 361 "Source \"%s\", " 362 "Target \"%s\", " 363 "lifetime %d, " 364 "mechanism %s, " 365 "flags 0x%08X, " 366 "local %d, " 367 "open %d", 368 context_handle, 369 source.c_str(), 370 target.c_str(), 371 lifetime_rec, 372 DescribeOid(gssapi_lib, 373 mech_type).c_str(), 374 ctx_flags, 375 locally_initiated, 376 open); 377 return description; 378 } 379 380 } // namespace 381 382 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name) 383 : initialized_(false), 384 gssapi_library_name_(gssapi_library_name), 385 gssapi_library_(NULL), 386 import_name_(NULL), 387 release_name_(NULL), 388 release_buffer_(NULL), 389 display_name_(NULL), 390 display_status_(NULL), 391 init_sec_context_(NULL), 392 wrap_size_limit_(NULL), 393 delete_sec_context_(NULL), 394 inquire_context_(NULL) { 395 } 396 397 GSSAPISharedLibrary::~GSSAPISharedLibrary() { 398 if (gssapi_library_) { 399 base::UnloadNativeLibrary(gssapi_library_); 400 gssapi_library_ = NULL; 401 } 402 } 403 404 bool GSSAPISharedLibrary::Init() { 405 if (!initialized_) 406 InitImpl(); 407 return initialized_; 408 } 409 410 bool GSSAPISharedLibrary::InitImpl() { 411 DCHECK(!initialized_); 412 #if defined(DLOPEN_KERBEROS) 413 gssapi_library_ = LoadSharedLibrary(); 414 if (gssapi_library_ == NULL) 415 return false; 416 #endif // defined(DLOPEN_KERBEROS) 417 initialized_ = true; 418 return true; 419 } 420 421 base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() { 422 const char* const* library_names; 423 size_t num_lib_names; 424 const char* user_specified_library[1]; 425 if (!gssapi_library_name_.empty()) { 426 user_specified_library[0] = gssapi_library_name_.c_str(); 427 library_names = user_specified_library; 428 num_lib_names = 1; 429 } else { 430 static const char* const kDefaultLibraryNames[] = { 431 #if defined(OS_MACOSX) 432 "libgssapi_krb5.dylib" // MIT Kerberos 433 #elif defined(OS_OPENBSD) 434 "libgssapi.so" // Heimdal - OpenBSD 435 #else 436 "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian 437 "libgssapi.so.4", // Heimdal - Suse10, MDK 438 "libgssapi.so.2", // Heimdal - Gentoo 439 "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10 440 #endif 441 }; 442 library_names = kDefaultLibraryNames; 443 num_lib_names = arraysize(kDefaultLibraryNames); 444 } 445 446 for (size_t i = 0; i < num_lib_names; ++i) { 447 const char* library_name = library_names[i]; 448 base::FilePath file_path(library_name); 449 450 // TODO(asanka): Move library loading to a separate thread. 451 // http://crbug.com/66702 452 base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily; 453 base::NativeLibrary lib = base::LoadNativeLibrary(file_path, NULL); 454 if (lib) { 455 // Only return this library if we can bind the functions we need. 456 if (BindMethods(lib)) 457 return lib; 458 base::UnloadNativeLibrary(lib); 459 } 460 } 461 LOG(WARNING) << "Unable to find a compatible GSSAPI library"; 462 return NULL; 463 } 464 465 #if defined(DLOPEN_KERBEROS) 466 #define BIND(lib, x) \ 467 DCHECK(lib); \ 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 #else 475 #define BIND(lib, x) gss_##x##_type x = gss_##x 476 #endif 477 478 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) { 479 BIND(lib, import_name); 480 BIND(lib, release_name); 481 BIND(lib, release_buffer); 482 BIND(lib, display_name); 483 BIND(lib, display_status); 484 BIND(lib, init_sec_context); 485 BIND(lib, wrap_size_limit); 486 BIND(lib, delete_sec_context); 487 BIND(lib, inquire_context); 488 489 import_name_ = import_name; 490 release_name_ = release_name; 491 release_buffer_ = release_buffer; 492 display_name_ = display_name; 493 display_status_ = display_status; 494 init_sec_context_ = init_sec_context; 495 wrap_size_limit_ = wrap_size_limit; 496 delete_sec_context_ = delete_sec_context; 497 inquire_context_ = inquire_context; 498 499 return true; 500 } 501 502 #undef BIND 503 504 OM_uint32 GSSAPISharedLibrary::import_name( 505 OM_uint32* minor_status, 506 const gss_buffer_t input_name_buffer, 507 const gss_OID input_name_type, 508 gss_name_t* output_name) { 509 DCHECK(initialized_); 510 return import_name_(minor_status, input_name_buffer, input_name_type, 511 output_name); 512 } 513 514 OM_uint32 GSSAPISharedLibrary::release_name( 515 OM_uint32* minor_status, 516 gss_name_t* input_name) { 517 DCHECK(initialized_); 518 return release_name_(minor_status, input_name); 519 } 520 521 OM_uint32 GSSAPISharedLibrary::release_buffer( 522 OM_uint32* minor_status, 523 gss_buffer_t buffer) { 524 DCHECK(initialized_); 525 return release_buffer_(minor_status, buffer); 526 } 527 528 OM_uint32 GSSAPISharedLibrary::display_name( 529 OM_uint32* minor_status, 530 const gss_name_t input_name, 531 gss_buffer_t output_name_buffer, 532 gss_OID* output_name_type) { 533 DCHECK(initialized_); 534 return display_name_(minor_status, 535 input_name, 536 output_name_buffer, 537 output_name_type); 538 } 539 540 OM_uint32 GSSAPISharedLibrary::display_status( 541 OM_uint32* minor_status, 542 OM_uint32 status_value, 543 int status_type, 544 const gss_OID mech_type, 545 OM_uint32* message_context, 546 gss_buffer_t status_string) { 547 DCHECK(initialized_); 548 return display_status_(minor_status, status_value, status_type, mech_type, 549 message_context, status_string); 550 } 551 552 OM_uint32 GSSAPISharedLibrary::init_sec_context( 553 OM_uint32* minor_status, 554 const gss_cred_id_t initiator_cred_handle, 555 gss_ctx_id_t* context_handle, 556 const gss_name_t target_name, 557 const gss_OID mech_type, 558 OM_uint32 req_flags, 559 OM_uint32 time_req, 560 const gss_channel_bindings_t input_chan_bindings, 561 const gss_buffer_t input_token, 562 gss_OID* actual_mech_type, 563 gss_buffer_t output_token, 564 OM_uint32* ret_flags, 565 OM_uint32* time_rec) { 566 DCHECK(initialized_); 567 return init_sec_context_(minor_status, 568 initiator_cred_handle, 569 context_handle, 570 target_name, 571 mech_type, 572 req_flags, 573 time_req, 574 input_chan_bindings, 575 input_token, 576 actual_mech_type, 577 output_token, 578 ret_flags, 579 time_rec); 580 } 581 582 OM_uint32 GSSAPISharedLibrary::wrap_size_limit( 583 OM_uint32* minor_status, 584 const gss_ctx_id_t context_handle, 585 int conf_req_flag, 586 gss_qop_t qop_req, 587 OM_uint32 req_output_size, 588 OM_uint32* max_input_size) { 589 DCHECK(initialized_); 590 return wrap_size_limit_(minor_status, 591 context_handle, 592 conf_req_flag, 593 qop_req, 594 req_output_size, 595 max_input_size); 596 } 597 598 OM_uint32 GSSAPISharedLibrary::delete_sec_context( 599 OM_uint32* minor_status, 600 gss_ctx_id_t* context_handle, 601 gss_buffer_t output_token) { 602 // This is called from the owner class' destructor, even if 603 // Init() is not called, so we can't assume |initialized_| 604 // is set. 605 if (!initialized_) 606 return 0; 607 return delete_sec_context_(minor_status, 608 context_handle, 609 output_token); 610 } 611 612 OM_uint32 GSSAPISharedLibrary::inquire_context( 613 OM_uint32* minor_status, 614 const gss_ctx_id_t context_handle, 615 gss_name_t* src_name, 616 gss_name_t* targ_name, 617 OM_uint32* lifetime_rec, 618 gss_OID* mech_type, 619 OM_uint32* ctx_flags, 620 int* locally_initiated, 621 int* open) { 622 DCHECK(initialized_); 623 return inquire_context_(minor_status, 624 context_handle, 625 src_name, 626 targ_name, 627 lifetime_rec, 628 mech_type, 629 ctx_flags, 630 locally_initiated, 631 open); 632 } 633 634 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib) 635 : security_context_(GSS_C_NO_CONTEXT), 636 gssapi_lib_(gssapi_lib) { 637 DCHECK(gssapi_lib_); 638 } 639 640 ScopedSecurityContext::~ScopedSecurityContext() { 641 if (security_context_ != GSS_C_NO_CONTEXT) { 642 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 643 OM_uint32 minor_status = 0; 644 OM_uint32 major_status = gssapi_lib_->delete_sec_context( 645 &minor_status, &security_context_, &output_token); 646 if (major_status != GSS_S_COMPLETE) { 647 LOG(WARNING) << "Problem releasing security_context. " 648 << DisplayStatus(major_status, minor_status); 649 } 650 security_context_ = GSS_C_NO_CONTEXT; 651 } 652 } 653 654 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, 655 const std::string& scheme, 656 gss_OID gss_oid) 657 : scheme_(scheme), 658 gss_oid_(gss_oid), 659 library_(library), 660 scoped_sec_context_(library), 661 can_delegate_(false) { 662 DCHECK(library_); 663 } 664 665 HttpAuthGSSAPI::~HttpAuthGSSAPI() { 666 } 667 668 bool HttpAuthGSSAPI::Init() { 669 if (!library_) 670 return false; 671 return library_->Init(); 672 } 673 674 bool HttpAuthGSSAPI::NeedsIdentity() const { 675 return decoded_server_auth_token_.empty(); 676 } 677 678 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const { 679 return false; 680 } 681 682 void HttpAuthGSSAPI::Delegate() { 683 can_delegate_ = true; 684 } 685 686 HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge( 687 HttpAuth::ChallengeTokenizer* tok) { 688 // Verify the challenge's auth-scheme. 689 if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str())) 690 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 691 692 std::string encoded_auth_token = tok->base64_param(); 693 694 if (encoded_auth_token.empty()) { 695 // If a context has already been established, an empty Negotiate challenge 696 // should be treated as a rejection of the current attempt. 697 if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT) 698 return HttpAuth::AUTHORIZATION_RESULT_REJECT; 699 DCHECK(decoded_server_auth_token_.empty()); 700 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 701 } else { 702 // If a context has not already been established, additional tokens should 703 // not be present in the auth challenge. 704 if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) 705 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 706 } 707 708 // Make sure the additional token is base64 encoded. 709 std::string decoded_auth_token; 710 bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token); 711 if (!base64_rv) 712 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 713 decoded_server_auth_token_ = decoded_auth_token; 714 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; 715 } 716 717 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials, 718 const std::string& spn, 719 std::string* auth_token) { 720 DCHECK(auth_token); 721 722 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 723 input_token.length = decoded_server_auth_token_.length(); 724 input_token.value = (input_token.length > 0) ? 725 const_cast<char*>(decoded_server_auth_token_.data()) : 726 NULL; 727 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 728 ScopedBuffer scoped_output_token(&output_token, library_); 729 int rv = GetNextSecurityToken(spn, &input_token, &output_token); 730 if (rv != OK) 731 return rv; 732 733 // Base64 encode data in output buffer and prepend the scheme. 734 std::string encode_input(static_cast<char*>(output_token.value), 735 output_token.length); 736 std::string encode_output; 737 base::Base64Encode(encode_input, &encode_output); 738 *auth_token = scheme_ + " " + encode_output; 739 return OK; 740 } 741 742 743 namespace { 744 745 // GSSAPI status codes consist of a calling error (essentially, a programmer 746 // bug), a routine error (defined by the RFC), and supplementary information, 747 // all bitwise-or'ed together in different regions of the 32 bit return value. 748 // This means a simple switch on the return codes is not sufficient. 749 750 int MapImportNameStatusToError(OM_uint32 major_status) { 751 VLOG(1) << "import_name returned 0x" << std::hex << major_status; 752 if (major_status == GSS_S_COMPLETE) 753 return OK; 754 if (GSS_CALLING_ERROR(major_status) != 0) 755 return ERR_UNEXPECTED; 756 OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status); 757 switch (routine_error) { 758 case GSS_S_FAILURE: 759 // Looking at the MIT Kerberos implementation, this typically is returned 760 // when memory allocation fails. However, the API does not guarantee 761 // that this is the case, so using ERR_UNEXPECTED rather than 762 // ERR_OUT_OF_MEMORY. 763 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 764 case GSS_S_BAD_NAME: 765 case GSS_S_BAD_NAMETYPE: 766 return ERR_MALFORMED_IDENTITY; 767 case GSS_S_DEFECTIVE_TOKEN: 768 // Not mentioned in the API, but part of code. 769 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 770 case GSS_S_BAD_MECH: 771 return ERR_UNSUPPORTED_AUTH_SCHEME; 772 default: 773 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 774 } 775 } 776 777 int MapInitSecContextStatusToError(OM_uint32 major_status) { 778 VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status; 779 // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like 780 // other code just checks if major_status is equivalent to it to indicate 781 // that there are no other errors included. 782 if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED) 783 return OK; 784 if (GSS_CALLING_ERROR(major_status) != 0) 785 return ERR_UNEXPECTED; 786 OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status); 787 switch (routine_status) { 788 case GSS_S_DEFECTIVE_TOKEN: 789 return ERR_INVALID_RESPONSE; 790 case GSS_S_DEFECTIVE_CREDENTIAL: 791 // Not expected since this implementation uses the default credential. 792 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 793 case GSS_S_BAD_SIG: 794 // Probably won't happen, but it's a bad response. 795 return ERR_INVALID_RESPONSE; 796 case GSS_S_NO_CRED: 797 return ERR_INVALID_AUTH_CREDENTIALS; 798 case GSS_S_CREDENTIALS_EXPIRED: 799 return ERR_INVALID_AUTH_CREDENTIALS; 800 case GSS_S_BAD_BINDINGS: 801 // This only happens with mutual authentication. 802 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 803 case GSS_S_NO_CONTEXT: 804 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 805 case GSS_S_BAD_NAMETYPE: 806 return ERR_UNSUPPORTED_AUTH_SCHEME; 807 case GSS_S_BAD_NAME: 808 return ERR_UNSUPPORTED_AUTH_SCHEME; 809 case GSS_S_BAD_MECH: 810 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; 811 case GSS_S_FAILURE: 812 // This should be an "Unexpected Security Status" according to the 813 // GSSAPI documentation, but it's typically used to indicate that 814 // credentials are not correctly set up on a user machine, such 815 // as a missing credential cache or hitting this after calling 816 // kdestroy. 817 // TODO(cbentzel): Use minor code for even better mapping? 818 return ERR_MISSING_AUTH_CREDENTIALS; 819 default: 820 if (routine_status != 0) 821 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 822 break; 823 } 824 OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status); 825 // Replays could indicate an attack. 826 if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN | 827 GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN)) 828 return ERR_INVALID_RESPONSE; 829 830 // At this point, every documented status has been checked. 831 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; 832 } 833 834 } 835 836 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn, 837 gss_buffer_t in_token, 838 gss_buffer_t out_token) { 839 // Create a name for the principal 840 // TODO(cbentzel): Just do this on the first pass? 841 std::string spn_principal = spn; 842 gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER; 843 spn_buffer.value = const_cast<char*>(spn_principal.c_str()); 844 spn_buffer.length = spn_principal.size() + 1; 845 OM_uint32 minor_status = 0; 846 gss_name_t principal_name = GSS_C_NO_NAME; 847 OM_uint32 major_status = library_->import_name( 848 &minor_status, 849 &spn_buffer, 850 GSS_C_NT_HOSTBASED_SERVICE, 851 &principal_name); 852 int rv = MapImportNameStatusToError(major_status); 853 if (rv != OK) { 854 LOG(ERROR) << "Problem importing name from " 855 << "spn \"" << spn_principal << "\"\n" 856 << DisplayExtendedStatus(library_, major_status, minor_status); 857 return rv; 858 } 859 ScopedName scoped_name(principal_name, library_); 860 861 // Continue creating a security context. 862 OM_uint32 req_flags = 0; 863 if (can_delegate_) 864 req_flags |= GSS_C_DELEG_FLAG; 865 major_status = library_->init_sec_context( 866 &minor_status, 867 GSS_C_NO_CREDENTIAL, 868 scoped_sec_context_.receive(), 869 principal_name, 870 gss_oid_, 871 req_flags, 872 GSS_C_INDEFINITE, 873 GSS_C_NO_CHANNEL_BINDINGS, 874 in_token, 875 NULL, // actual_mech_type 876 out_token, 877 NULL, // ret flags 878 NULL); 879 rv = MapInitSecContextStatusToError(major_status); 880 if (rv != OK) { 881 LOG(ERROR) << "Problem initializing context. \n" 882 << DisplayExtendedStatus(library_, major_status, minor_status) 883 << '\n' 884 << DescribeContext(library_, scoped_sec_context_.get()); 885 } 886 return rv; 887 } 888 889 } // namespace net 890