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(src_name
, gssapi_lib
);
349 ScopedName(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 "libgssapi_krb5.dylib" // MIT Kerberos
434 #elif defined(OS_OPENBSD)
435 "libgssapi.so" // Heimdal - OpenBSD
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
443 library_names
= kDefaultLibraryNames
;
444 num_lib_names
= arraysize(kDefaultLibraryNames
);
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
);
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
);
456 // Only return this library if we can bind the functions we need.
457 if (BindMethods(lib
))
459 base::UnloadNativeLibrary(lib
);
462 LOG(WARNING
) << "Unable to find a compatible GSSAPI library";
466 #if defined(DLOPEN_KERBEROS)
467 #define BIND(lib, x) \
469 gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \
470 base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \
472 LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \
476 #define BIND(lib, x) gss_##x##_type x = gss_##x
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
);
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
;
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
,
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
);
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
);
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
,
541 OM_uint32
GSSAPISharedLibrary::display_status(
542 OM_uint32
* minor_status
,
543 OM_uint32 status_value
,
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
);
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
,
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
,
583 OM_uint32
GSSAPISharedLibrary::wrap_size_limit(
584 OM_uint32
* minor_status
,
585 const gss_ctx_id_t context_handle
,
588 OM_uint32 req_output_size
,
589 OM_uint32
* max_input_size
) {
590 DCHECK(initialized_
);
591 return wrap_size_limit_(minor_status
,
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_|
608 return delete_sec_context_(minor_status
,
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
,
620 OM_uint32
* ctx_flags
,
621 int* locally_initiated
,
623 DCHECK(initialized_
);
624 return inquire_context_(minor_status
,
635 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary
* gssapi_lib
)
636 : security_context_(GSS_C_NO_CONTEXT
),
637 gssapi_lib_(gssapi_lib
) {
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
);
651 security_context_
= GSS_C_NO_CONTEXT
;
655 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary
* library
,
656 const std::string
& scheme
,
661 scoped_sec_context_(library
),
662 can_delegate_(false) {
666 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
669 bool HttpAuthGSSAPI::Init() {
672 return library_
->Init();
675 bool HttpAuthGSSAPI::NeedsIdentity() const {
676 return decoded_server_auth_token_
.empty();
679 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
683 void HttpAuthGSSAPI::Delegate() {
684 can_delegate_
= true;
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
;
693 std::string encoded_auth_token
= tok
->base64_param();
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
;
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
;
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
);
713 return HttpAuth::AUTHORIZATION_RESULT_INVALID
;
714 decoded_server_auth_token_
= decoded_auth_token
;
715 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT
;
718 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials
* credentials
,
719 const std::string
& spn
,
720 std::string
* auth_token
) {
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()) :
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
);
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
;
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.
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
)
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
) {
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
;
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
;
772 return ERR_UNSUPPORTED_AUTH_SCHEME
;
774 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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
)
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
;
795 // Probably won't happen, but it's a bad response.
796 return ERR_INVALID_RESPONSE
;
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
;
809 return ERR_UNSUPPORTED_AUTH_SCHEME
;
811 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
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
818 // TODO(cbentzel): Use minor code for even better mapping?
819 return ERR_MISSING_AUTH_CREDENTIALS
;
821 if (routine_status
!= 0)
822 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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
;
831 // At this point, every documented status has been checked.
832 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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(
851 GSS_C_NT_HOSTBASED_SERVICE
,
853 int rv
= MapImportNameStatusToError(major_status
);
855 LOG(ERROR
) << "Problem importing name from "
856 << "spn \"" << spn_principal
<< "\"\n"
857 << DisplayExtendedStatus(library_
, major_status
, minor_status
);
860 ScopedName
scoped_name(principal_name
, library_
);
862 // Continue creating a security context.
863 OM_uint32 req_flags
= 0;
865 req_flags
|= GSS_C_DELEG_FLAG
;
866 major_status
= library_
->init_sec_context(
869 scoped_sec_context_
.receive(),
874 GSS_C_NO_CHANNEL_BINDINGS
,
876 NULL
, // actual_mech_type
880 rv
= MapInitSecContextStatusToError(major_status
);
882 LOG(ERROR
) << "Problem initializing context. \n"
883 << DisplayExtendedStatus(library_
, major_status
, minor_status
)
885 << DescribeContext(library_
, scoped_sec_context_
.get());