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"
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.
28 static gss_OID_desc GSS_C_NT_USER_NAME_VAL
= {
30 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
32 static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL
= {
34 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
36 static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL
= {
38 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
40 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL
= {
42 const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
44 static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL
= {
46 const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
48 static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL
= {
50 const_cast<char*>("\x2b\x06\01\x05\x06\x03")
52 static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL
= {
54 const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
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
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
;
79 // Exported mechanism for GSSAPI. We always use SPNEGO:
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
= {
84 const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
87 gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC
=
88 &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL
;
93 std::string
DisplayStatus(OM_uint32 major_status
,
94 OM_uint32 minor_status
) {
95 if (major_status
== GSS_S_COMPLETE
)
97 return base::StringPrintf("0x%08X 0x%08X", major_status
, minor_status
);
100 std::string
DisplayCode(GSSAPILibrary
* gssapi_lib
,
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
);
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
;
117 gss_buffer_desc_struct msg
= GSS_C_EMPTY_BUFFER
;
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
));
130 gssapi_lib
->release_buffer(&min_stat
, &msg
);
137 std::string
DisplayExtendedStatus(GSSAPILibrary
* gssapi_lib
,
138 OM_uint32 major_status
,
139 OM_uint32 minor_status
) {
140 if (major_status
== GSS_S_COMPLETE
)
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(),
148 // ScopedName releases a gss_name_t when it goes out of scope.
151 ScopedName(gss_name_t name
,
152 GSSAPILibrary
* gssapi_lib
)
154 gssapi_lib_(gssapi_lib
) {
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
);
167 name_
= GSS_C_NO_NAME
;
173 GSSAPILibrary
* gssapi_lib_
;
175 DISALLOW_COPY_AND_ASSIGN(ScopedName
);
178 // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
181 ScopedBuffer(gss_buffer_t buffer
,
182 GSSAPILibrary
* gssapi_lib
)
184 gssapi_lib_(gssapi_lib
) {
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
);
197 buffer_
= GSS_C_NO_BUFFER
;
202 gss_buffer_t buffer_
;
203 GSSAPILibrary
* gssapi_lib_
;
205 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer
);
210 std::string
AppendIfPredefinedValue(gss_OID oid
,
211 gss_OID predefined_oid
,
212 const char* predefined_oid_name
) {
214 DCHECK(predefined_oid
);
215 DCHECK(predefined_oid_name
);
217 if (oid
->length
!= predefined_oid
->length
)
219 if (0 != memcmp(oid
->elements
,
220 predefined_oid
->elements
,
221 predefined_oid
->length
))
225 output
+= predefined_oid_name
;
232 std::string
DescribeOid(GSSAPILibrary
* gssapi_lib
, const gss_OID oid
) {
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
]))
249 if (!str
[str_length
]) {
250 output
+= base::StringPrintf("\"%s\"", str
);
254 output
= base::StringPrintf("(%u) \"", byte_length
);
255 if (!oid
->elements
) {
259 const unsigned char* elements
=
260 reinterpret_cast<const unsigned char*>(oid
->elements
);
261 // Don't print more than |kMaxCharsToPrint| characters.
263 for ( ; (i
< byte_length
) && (i
< kMaxCharsToPrint
); ++i
) {
264 output
+= base::StringPrintf("\\x%02X", elements
[i
]);
266 if (i
>= kMaxCharsToPrint
)
270 // Check if the OID is one of the predefined values.
271 output
+= AppendIfPredefinedValue(oid
,
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
,
288 "GSS_C_NT_ANONYMOUS");
289 output
+= AppendIfPredefinedValue(oid
,
290 GSS_C_NT_EXPORT_NAME
,
291 "GSS_C_NT_EXPORT_NAME");
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
,
306 ScopedBuffer
scoped_output_name(&output_name_buffer
, gssapi_lib
);
307 if (major_status
!= GSS_S_COMPLETE
) {
309 base::StringPrintf("Unable to describe name 0x%p, %s",
311 DisplayExtendedStatus(gssapi_lib
,
313 minor_status
).c_str());
316 int len
= output_name_buffer
.length
;
317 std::string description
= base::StringPrintf(
320 reinterpret_cast<const char*>(output_name_buffer
.value
),
321 DescribeOid(gssapi_lib
, output_name_type
).c_str());
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;
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
,
347 ScopedName(src_name
, gssapi_lib
);
348 ScopedName(targ_name
, gssapi_lib
);
349 if (major_status
!= GSS_S_COMPLETE
) {
351 base::StringPrintf("Unable to describe context 0x%p, %s",
353 DisplayExtendedStatus(gssapi_lib
,
355 minor_status
).c_str());
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: "
372 DescribeOid(gssapi_lib
,
382 GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string
& gssapi_library_name
)
383 : initialized_(false),
384 gssapi_library_name_(gssapi_library_name
),
385 gssapi_library_(NULL
),
388 release_buffer_(NULL
),
390 display_status_(NULL
),
391 init_sec_context_(NULL
),
392 wrap_size_limit_(NULL
),
393 delete_sec_context_(NULL
),
394 inquire_context_(NULL
) {
397 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
398 if (gssapi_library_
) {
399 base::UnloadNativeLibrary(gssapi_library_
);
400 gssapi_library_
= NULL
;
404 bool GSSAPISharedLibrary::Init() {
410 bool GSSAPISharedLibrary::InitImpl() {
411 DCHECK(!initialized_
);
412 #if defined(DLOPEN_KERBEROS)
413 gssapi_library_
= LoadSharedLibrary();
414 if (gssapi_library_
== NULL
)
416 #endif // defined(DLOPEN_KERBEROS)
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
;
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
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
442 library_names
= kDefaultLibraryNames
;
443 num_lib_names
= arraysize(kDefaultLibraryNames
);
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
);
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
);
455 // Only return this library if we can bind the functions we need.
456 if (BindMethods(lib
))
458 base::UnloadNativeLibrary(lib
);
461 LOG(WARNING
) << "Unable to find a compatible GSSAPI library";
465 #if defined(DLOPEN_KERBEROS)
466 #define BIND(lib, x) \
468 gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \
469 base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \
471 LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \
475 #define BIND(lib, x) gss_##x##_type x = gss_##x
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
);
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
;
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
,
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
);
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
);
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
,
540 OM_uint32
GSSAPISharedLibrary::display_status(
541 OM_uint32
* minor_status
,
542 OM_uint32 status_value
,
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
);
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
,
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
,
582 OM_uint32
GSSAPISharedLibrary::wrap_size_limit(
583 OM_uint32
* minor_status
,
584 const gss_ctx_id_t context_handle
,
587 OM_uint32 req_output_size
,
588 OM_uint32
* max_input_size
) {
589 DCHECK(initialized_
);
590 return wrap_size_limit_(minor_status
,
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_|
607 return delete_sec_context_(minor_status
,
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
,
619 OM_uint32
* ctx_flags
,
620 int* locally_initiated
,
622 DCHECK(initialized_
);
623 return inquire_context_(minor_status
,
634 ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary
* gssapi_lib
)
635 : security_context_(GSS_C_NO_CONTEXT
),
636 gssapi_lib_(gssapi_lib
) {
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
);
650 security_context_
= GSS_C_NO_CONTEXT
;
654 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary
* library
,
655 const std::string
& scheme
,
660 scoped_sec_context_(library
),
661 can_delegate_(false) {
665 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
668 bool HttpAuthGSSAPI::Init() {
671 return library_
->Init();
674 bool HttpAuthGSSAPI::NeedsIdentity() const {
675 return decoded_server_auth_token_
.empty();
678 bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
682 void HttpAuthGSSAPI::Delegate() {
683 can_delegate_
= true;
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
;
692 std::string encoded_auth_token
= tok
->base64_param();
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
;
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
;
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
);
712 return HttpAuth::AUTHORIZATION_RESULT_INVALID
;
713 decoded_server_auth_token_
= decoded_auth_token
;
714 return HttpAuth::AUTHORIZATION_RESULT_ACCEPT
;
717 int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials
* credentials
,
718 const std::string
& spn
,
719 std::string
* auth_token
) {
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()) :
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
);
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
;
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.
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
)
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
) {
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
;
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
;
771 return ERR_UNSUPPORTED_AUTH_SCHEME
;
773 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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
)
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
;
794 // Probably won't happen, but it's a bad response.
795 return ERR_INVALID_RESPONSE
;
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
;
808 return ERR_UNSUPPORTED_AUTH_SCHEME
;
810 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS
;
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
817 // TODO(cbentzel): Use minor code for even better mapping?
818 return ERR_MISSING_AUTH_CREDENTIALS
;
820 if (routine_status
!= 0)
821 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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
;
830 // At this point, every documented status has been checked.
831 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS
;
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(
850 GSS_C_NT_HOSTBASED_SERVICE
,
852 int rv
= MapImportNameStatusToError(major_status
);
854 LOG(ERROR
) << "Problem importing name from "
855 << "spn \"" << spn_principal
<< "\"\n"
856 << DisplayExtendedStatus(library_
, major_status
, minor_status
);
859 ScopedName
scoped_name(principal_name
, library_
);
861 // Continue creating a security context.
862 OM_uint32 req_flags
= 0;
864 req_flags
|= GSS_C_DELEG_FLAG
;
865 major_status
= library_
->init_sec_context(
868 scoped_sec_context_
.receive(),
873 GSS_C_NO_CHANNEL_BINDINGS
,
875 NULL
, // actual_mech_type
879 rv
= MapInitSecContextStatusToError(major_status
);
881 LOG(ERROR
) << "Problem initializing context. \n"
882 << DisplayExtendedStatus(library_
, major_status
, minor_status
)
884 << DescribeContext(library_
, scoped_sec_context_
.get());