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.
5 #include "net/http/http_auth_gssapi_posix.h"
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"
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.
29 static gss_OID_desc GSS_C_NT_USER_NAME_VAL
= {
31 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
33 static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL
= {
35 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
37 static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL
= {
39 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
41 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL
= {
43 const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
45 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL
= {
47 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
49 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL
= {
51 const_cast<char*>("\x2b\x06\01\x05\x06\x03")
53 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL
= {
55 const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
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
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
;
80 // Exported mechanism for GSSAPI. We always use SPNEGO:
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
= {
85 const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
88 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC
=
89 &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL
;
94 std::string
DisplayStatus(OM_uint32 major_status
,
95 OM_uint32 minor_status
) {
96 if (major_status
== GSS_S_COMPLETE
)
98 return base::StringPrintf("0x%08X 0x%08X", major_status
, minor_status
);
101 std::string
DisplayCode(GSSAPILibrary
* gssapi_lib
,
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
);
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
;
118 gss_buffer_desc_struct msg
= GSS_C_EMPTY_BUFFER
;
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
));
131 gssapi_lib
->release_buffer(&min_stat
, &msg
);
138 std::string
DisplayExtendedStatus(GSSAPILibrary
* gssapi_lib
,
139 OM_uint32 major_status
,
140 OM_uint32 minor_status
) {
141 if (major_status
== GSS_S_COMPLETE
)
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(),
149 // ScopedName releases a gss_name_t when it goes out of scope.
152 ScopedName(gss_name_t name
,
153 GSSAPILibrary
* gssapi_lib
)
155 gssapi_lib_(gssapi_lib
) {
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
);
168 name_
= GSS_C_NO_NAME
;
174 GSSAPILibrary
* gssapi_lib_
;
176 DISALLOW_COPY_AND_ASSIGN(ScopedName
);
179 // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
182 ScopedBuffer(gss_buffer_t buffer
,
183 GSSAPILibrary
* gssapi_lib
)
185 gssapi_lib_(gssapi_lib
) {
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
);
198 buffer_
= GSS_C_NO_BUFFER
;
203 gss_buffer_t buffer_
;
204 GSSAPILibrary
* gssapi_lib_
;
206 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer
);
211 std::string
AppendIfPredefinedValue(gss_OID oid
,
212 gss_OID predefined_oid
,
213 const char* predefined_oid_name
) {
215 DCHECK(predefined_oid
);
216 DCHECK(predefined_oid_name
);
218 if (oid
->length
!= predefined_oid
->length
)
220 if (0 != memcmp(oid
->elements
,
221 predefined_oid
->elements
,
222 predefined_oid
->length
))
226 output
+= predefined_oid_name
;
233 std::string
DescribeOid(GSSAPILibrary
* gssapi_lib
, const gss_OID oid
) {
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
]))
250 if (!str
[str_length
]) {
251 output
+= base::StringPrintf("\"%s\"", str
);
255 output
= base::StringPrintf("(%u) \"", byte_length
);
256 if (!oid
->elements
) {
260 const unsigned char* elements
=
261 reinterpret_cast<const unsigned char*>(oid
->elements
);
262 // Don't print more than |kMaxCharsToPrint| characters.
264 for ( ; (i
< byte_length
) && (i
< kMaxCharsToPrint
); ++i
) {
265 output
+= base::StringPrintf("\\x%02X", elements
[i
]);
267 if (i
>= kMaxCharsToPrint
)
271 // Check if the OID is one of the predefined values.
272 output
+= AppendIfPredefinedValue(oid
,
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
,
289 "GSS_C_NT_ANONYMOUS");
290 output
+= AppendIfPredefinedValue(oid
,
291 GSS_C_NT_EXPORT_NAME
,
292 "GSS_C_NT_EXPORT_NAME");
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
,
307 ScopedBuffer
scoped_output_name(&output_name_buffer
, gssapi_lib
);
308 if (major_status
!= GSS_S_COMPLETE
) {
310 base::StringPrintf("Unable to describe name 0x%p, %s",
312 DisplayExtendedStatus(gssapi_lib
,
314 minor_status
).c_str());
317 int len
= output_name_buffer
.length
;
318 std::string description
= base::StringPrintf(
321 reinterpret_cast<const char*>(output_name_buffer
.value
),
322 DescribeOid(gssapi_lib
, output_name_type
).c_str());
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;
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
,
348 ScopedName
scoped_src_name(src_name
, gssapi_lib
);
349 ScopedName
scoped_targ_name(targ_name
, gssapi_lib
);
350 if (major_status
!= GSS_S_COMPLETE
) {
352 base::StringPrintf("Unable to describe context 0x%p, %s",
354 DisplayExtendedStatus(gssapi_lib
,
356 minor_status
).c_str());
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: "
373 DescribeOid(gssapi_lib
,
383 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string
& gssapi_library_name
)
384 : initialized_(false),
385 gssapi_library_name_(gssapi_library_name
),
386 gssapi_library_(NULL
),
389 release_buffer_(NULL
),
391 display_status_(NULL
),
392 init_sec_context_(NULL
),
393 wrap_size_limit_(NULL
),
394 delete_sec_context_(NULL
),
395 inquire_context_(NULL
) {
398 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
399 if (gssapi_library_
) {
400 base::UnloadNativeLibrary(gssapi_library_
);
401 gssapi_library_
= NULL
;
405 bool GSSAPISharedLibrary::Init() {
411 bool GSSAPISharedLibrary::InitImpl() {
412 DCHECK(!initialized_
);
413 #if defined(DLOPEN_KERBEROS)
414 gssapi_library_
= LoadSharedLibrary();
415 if (gssapi_library_
== NULL
)
417 #endif // defined(DLOPEN_KERBEROS)
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
;
431 static const char* const kDefaultLibraryNames
[] = {
432 #if defined(OS_MACOSX)
433 // This library is provided by Kerberos.framework.
434 "libgssapi_krb5.dylib"
435 #elif defined(OS_OPENBSD)
436 "libgssapi.so" // Heimdal - OpenBSD
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
444 library_names
= kDefaultLibraryNames
;
445 num_lib_names
= arraysize(kDefaultLibraryNames
);
448 for (size_t i
= 0; i
< num_lib_names
; ++i
) {
449 const char* library_name
= library_names
[i
];
450 base::FilePath
file_path(library_name
);
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
);
457 // Only return this library if we can bind the functions we need.
458 if (BindMethods(lib
))
460 base::UnloadNativeLibrary(lib
);
463 LOG(WARNING
) << "Unable to find a compatible GSSAPI library";
467 #if defined(DLOPEN_KERBEROS)
468 #define BIND(lib, x) \
470 gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \
471 base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \
473 LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \
477 #define BIND(lib, x) gss_##x##_type x = gss_##x
480 bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib
) {
481 BIND(lib
, import_name
);
482 BIND(lib
, release_name
);
483 BIND(lib
, release_buffer
);
484 BIND(lib
, display_name
);
485 BIND(lib
, display_status
);
486 BIND(lib
, init_sec_context
);
487 BIND(lib
, wrap_size_limit
);
488 BIND(lib
, delete_sec_context
);
489 BIND(lib
, inquire_context
);
491 import_name_
= import_name
;
492 release_name_
= release_name
;
493 release_buffer_
= release_buffer
;
494 display_name_
= display_name
;
495 display_status_
= display_status
;
496 init_sec_context_
= init_sec_context
;
497 wrap_size_limit_
= wrap_size_limit
;
498 delete_sec_context_
= delete_sec_context
;
499 inquire_context_
= inquire_context
;
506 OM_uint32
GSSAPISharedLibrary::import_name(
507 OM_uint32
* minor_status
,
508 const gss_buffer_t input_name_buffer
,
509 const gss_OID input_name_type
,
510 gss_name_t
* output_name
) {
511 DCHECK(initialized_
);
512 return import_name_(minor_status
, input_name_buffer
, input_name_type
,
516 OM_uint32
GSSAPISharedLibrary::release_name(
517 OM_uint32
* minor_status
,
518 gss_name_t
* input_name
) {
519 DCHECK(initialized_
);
520 return release_name_(minor_status
, input_name
);
523 OM_uint32
GSSAPISharedLibrary::release_buffer(
524 OM_uint32
* minor_status
,
525 gss_buffer_t buffer
) {
526 DCHECK(initialized_
);
527 return release_buffer_(minor_status
, buffer
);
530 OM_uint32
GSSAPISharedLibrary::display_name(
531 OM_uint32
* minor_status
,
532 const gss_name_t input_name
,
533 gss_buffer_t output_name_buffer
,
534 gss_OID
* output_name_type
) {
535 DCHECK(initialized_
);
536 return display_name_(minor_status
,
542 OM_uint32
GSSAPISharedLibrary::display_status(
543 OM_uint32
* minor_status
,
544 OM_uint32 status_value
,
546 const gss_OID mech_type
,
547 OM_uint32
* message_context
,
548 gss_buffer_t status_string
) {
549 DCHECK(initialized_
);
550 return display_status_(minor_status
, status_value
, status_type
, mech_type
,
551 message_context
, status_string
);
554 OM_uint32
GSSAPISharedLibrary::init_sec_context(
555 OM_uint32
* minor_status
,
556 const gss_cred_id_t initiator_cred_handle
,
557 gss_ctx_id_t
* context_handle
,
558 const gss_name_t target_name
,
559 const gss_OID mech_type
,
562 const gss_channel_bindings_t input_chan_bindings
,
563 const gss_buffer_t input_token
,
564 gss_OID
* actual_mech_type
,
565 gss_buffer_t output_token
,
566 OM_uint32
* ret_flags
,
567 OM_uint32
* time_rec
) {
568 DCHECK(initialized_
);
569 return init_sec_context_(minor_status
,
570 initiator_cred_handle
,
584 OM_uint32
GSSAPISharedLibrary::wrap_size_limit(
585 OM_uint32
* minor_status
,
586 const gss_ctx_id_t context_handle
,
589 OM_uint32 req_output_size
,
590 OM_uint32
* max_input_size
) {
591 DCHECK(initialized_
);
592 return wrap_size_limit_(minor_status
,
600 OM_uint32
GSSAPISharedLibrary::delete_sec_context(
601 OM_uint32
* minor_status
,
602 gss_ctx_id_t
* context_handle
,
603 gss_buffer_t output_token
) {
604 // This is called from the owner class' destructor, even if
605 // Init() is not called, so we can't assume |initialized_|
609 return delete_sec_context_(minor_status
,
614 OM_uint32
GSSAPISharedLibrary::inquire_context(
615 OM_uint32
* minor_status
,
616 const gss_ctx_id_t context_handle
,
617 gss_name_t
* src_name
,
618 gss_name_t
* targ_name
,
619 OM_uint32
* lifetime_rec
,
621 OM_uint32
* ctx_flags
,
622 int* locally_initiated
,
624 DCHECK(initialized_
);
625 return inquire_context_(minor_status
,
636 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary
* gssapi_lib
)
637 : security_context_(GSS_C_NO_CONTEXT
),
638 gssapi_lib_(gssapi_lib
) {
642 ScopedSecurityContext::~ScopedSecurityContext() {
643 if (security_context_
!= GSS_C_NO_CONTEXT
) {
644 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
645 OM_uint32 minor_status
= 0;
646 OM_uint32 major_status
= gssapi_lib_
->delete_sec_context(
647 &minor_status
, &security_context_
, &output_token
);
648 if (major_status
!= GSS_S_COMPLETE
) {
649 LOG(WARNING
) << "Problem releasing security_context. "
650 << DisplayStatus(major_status
, minor_status
);
652 security_context_
= GSS_C_NO_CONTEXT
;
656 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary
* library
,
657 const std::string
& scheme
,
662 scoped_sec_context_(library
),
663 can_delegate_(false) {
667 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
670 bool HttpAuthGSSAPI::Init() {
673 return library_
->Init();
676 bool HttpAuthGSSAPI::NeedsIdentity() const {
677 return decoded_server_auth_token_
.empty();
680 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
684 void HttpAuthGSSAPI::Delegate() {
685 can_delegate_
= true;
688 HttpAuth::AuthorizationResult
HttpAuthGSSAPI::ParseChallenge(
689 HttpAuthChallengeTokenizer
* tok
) {
690 // Verify the challenge's auth-scheme.
691 if (!LowerCaseEqualsASCII(tok
->scheme(),
692 base::StringToLowerASCII(scheme_
).c_str()))
693 return HttpAuth::AUTHORIZATION_RESULT_INVALID
;
695 std::string encoded_auth_token
= tok
->base64_param();
697 if (encoded_auth_token
.empty()) {
698 // If a context has already been established, an empty Negotiate challenge
699 // should be treated as a rejection of the current attempt.
700 if (scoped_sec_context_
.get() != GSS_C_NO_CONTEXT
)
701 return HttpAuth::AUTHORIZATION_RESULT_REJECT
;
702 DCHECK(decoded_server_auth_token_
.empty());
703 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT
;
705 // If a context has not already been established, additional tokens should
706 // not be present in the auth challenge.
707 if (scoped_sec_context_
.get() == GSS_C_NO_CONTEXT
)
708 return HttpAuth::AUTHORIZATION_RESULT_INVALID
;
711 // Make sure the additional token is base64 encoded.
712 std::string decoded_auth_token
;
713 bool base64_rv
= base::Base64Decode(encoded_auth_token
, &decoded_auth_token
);
715 return HttpAuth::AUTHORIZATION_RESULT_INVALID
;
716 decoded_server_auth_token_
= decoded_auth_token
;
717 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT
;
720 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials
* credentials
,
721 const std::string
& spn
,
722 std::string
* auth_token
) {
725 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
726 input_token
.length
= decoded_server_auth_token_
.length();
727 input_token
.value
= (input_token
.length
> 0) ?
728 const_cast<char*>(decoded_server_auth_token_
.data()) :
730 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
731 ScopedBuffer
scoped_output_token(&output_token
, library_
);
732 int rv
= GetNextSecurityToken(spn
, &input_token
, &output_token
);
736 // Base64 encode data in output buffer and prepend the scheme.
737 std::string
encode_input(static_cast<char*>(output_token
.value
),
738 output_token
.length
);
739 std::string encode_output
;
740 base::Base64Encode(encode_input
, &encode_output
);
741 *auth_token
= scheme_
+ " " + encode_output
;
748 // GSSAPI status codes consist of a calling error (essentially, a programmer
749 // bug), a routine error (defined by the RFC), and supplementary information,
750 // all bitwise-or'ed together in different regions of the 32 bit return value.
751 // This means a simple switch on the return codes is not sufficient.
753 int MapImportNameStatusToError(OM_uint32 major_status
) {
754 VLOG(1) << "import_name returned 0x" << std::hex
<< major_status
;
755 if (major_status
== GSS_S_COMPLETE
)
757 if (GSS_CALLING_ERROR(major_status
) != 0)
758 return ERR_UNEXPECTED
;
759 OM_uint32 routine_error
= GSS_ROUTINE_ERROR(major_status
);
760 switch (routine_error
) {
762 // Looking at the MIT Kerberos implementation, this typically is returned
763 // when memory allocation fails. However, the API does not guarantee
764 // that this is the case, so using ERR_UNEXPECTED rather than
765 // ERR_OUT_OF_MEMORY.
766 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
768 case GSS_S_BAD_NAMETYPE
:
769 return ERR_MALFORMED_IDENTITY
;
770 case GSS_S_DEFECTIVE_TOKEN
:
771 // Not mentioned in the API, but part of code.
772 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
774 return ERR_UNSUPPORTED_AUTH_SCHEME
;
776 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
780 int MapInitSecContextStatusToError(OM_uint32 major_status
) {
781 VLOG(1) << "init_sec_context returned 0x" << std::hex
<< major_status
;
782 // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
783 // other code just checks if major_status is equivalent to it to indicate
784 // that there are no other errors included.
785 if (major_status
== GSS_S_COMPLETE
|| major_status
== GSS_S_CONTINUE_NEEDED
)
787 if (GSS_CALLING_ERROR(major_status
) != 0)
788 return ERR_UNEXPECTED
;
789 OM_uint32 routine_status
= GSS_ROUTINE_ERROR(major_status
);
790 switch (routine_status
) {
791 case GSS_S_DEFECTIVE_TOKEN
:
792 return ERR_INVALID_RESPONSE
;
793 case GSS_S_DEFECTIVE_CREDENTIAL
:
794 // Not expected since this implementation uses the default credential.
795 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
797 // Probably won't happen, but it's a bad response.
798 return ERR_INVALID_RESPONSE
;
800 return ERR_INVALID_AUTH_CREDENTIALS
;
801 case GSS_S_CREDENTIALS_EXPIRED
:
802 return ERR_INVALID_AUTH_CREDENTIALS
;
803 case GSS_S_BAD_BINDINGS
:
804 // This only happens with mutual authentication.
805 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
806 case GSS_S_NO_CONTEXT
:
807 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
808 case GSS_S_BAD_NAMETYPE
:
809 return ERR_UNSUPPORTED_AUTH_SCHEME
;
811 return ERR_UNSUPPORTED_AUTH_SCHEME
;
813 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
815 // This should be an "Unexpected Security Status" according to the
816 // GSSAPI documentation, but it's typically used to indicate that
817 // credentials are not correctly set up on a user machine, such
818 // as a missing credential cache or hitting this after calling
820 // TODO(cbentzel): Use minor code for even better mapping?
821 return ERR_MISSING_AUTH_CREDENTIALS
;
823 if (routine_status
!= 0)
824 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
827 OM_uint32 supplemental_status
= GSS_SUPPLEMENTARY_INFO(major_status
);
828 // Replays could indicate an attack.
829 if (supplemental_status
& (GSS_S_DUPLICATE_TOKEN
| GSS_S_OLD_TOKEN
|
830 GSS_S_UNSEQ_TOKEN
| GSS_S_GAP_TOKEN
))
831 return ERR_INVALID_RESPONSE
;
833 // At this point, every documented status has been checked.
834 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
839 int HttpAuthGSSAPI::GetNextSecurityToken(const std::string
& spn
,
840 gss_buffer_t in_token
,
841 gss_buffer_t out_token
) {
842 // Create a name for the principal
843 // TODO(cbentzel): Just do this on the first pass?
844 std::string spn_principal
= spn
;
845 gss_buffer_desc spn_buffer
= GSS_C_EMPTY_BUFFER
;
846 spn_buffer
.value
= const_cast<char*>(spn_principal
.c_str());
847 spn_buffer
.length
= spn_principal
.size() + 1;
848 OM_uint32 minor_status
= 0;
849 gss_name_t principal_name
= GSS_C_NO_NAME
;
850 OM_uint32 major_status
= library_
->import_name(
853 GSS_C_NT_HOSTBASED_SERVICE
,
855 int rv
= MapImportNameStatusToError(major_status
);
857 LOG(ERROR
) << "Problem importing name from "
858 << "spn \"" << spn_principal
<< "\"\n"
859 << DisplayExtendedStatus(library_
, major_status
, minor_status
);
862 ScopedName
scoped_name(principal_name
, library_
);
864 // Continue creating a security context.
865 OM_uint32 req_flags
= 0;
867 req_flags
|= GSS_C_DELEG_FLAG
;
868 major_status
= library_
->init_sec_context(
871 scoped_sec_context_
.receive(),
876 GSS_C_NO_CHANNEL_BINDINGS
,
878 NULL
, // actual_mech_type
882 rv
= MapInitSecContextStatusToError(major_status
);
884 LOG(ERROR
) << "Problem initializing context. \n"
885 << DisplayExtendedStatus(library_
, major_status
, minor_status
)
887 << DescribeContext(library_
, scoped_sec_context_
.get());