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