summaryrefslogtreecommitdiffstats
path: root/extensions/auth
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /extensions/auth
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'extensions/auth')
-rw-r--r--extensions/auth/gssapi.h848
-rw-r--r--extensions/auth/moz.build27
-rw-r--r--extensions/auth/nsAuth.h27
-rw-r--r--extensions/auth/nsAuthFactory.cpp247
-rw-r--r--extensions/auth/nsAuthGSSAPI.cpp603
-rw-r--r--extensions/auth/nsAuthGSSAPI.h63
-rw-r--r--extensions/auth/nsAuthSASL.cpp151
-rw-r--r--extensions/auth/nsAuthSASL.h38
-rw-r--r--extensions/auth/nsAuthSSPI.cpp666
-rw-r--r--extensions/auth/nsAuthSSPI.h59
-rw-r--r--extensions/auth/nsAuthSambaNTLM.cpp296
-rw-r--r--extensions/auth/nsAuthSambaNTLM.h52
-rw-r--r--extensions/auth/nsHttpNegotiateAuth.cpp731
-rw-r--r--extensions/auth/nsHttpNegotiateAuth.h44
14 files changed, 3852 insertions, 0 deletions
diff --git a/extensions/auth/gssapi.h b/extensions/auth/gssapi.h
new file mode 100644
index 000000000..a3ce3d8c5
--- /dev/null
+++ b/extensions/auth/gssapi.h
@@ -0,0 +1,848 @@
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ ****** END LICENSE BLOCK ***** */
+
+#ifndef GSSAPI_H_
+#define GSSAPI_H_
+
+/*
+ * Also define _GSSAPI_H_ as that is what the Kerberos 5 code defines and
+ * what header files on some systems look for.
+ */
+#define _GSSAPI_H_
+
+/*
+ * On Mac OS X, Kerberos/Kerberos.h is used to gain access to certain
+ * system-specific Kerberos functions, but on 10.4, that file also brings
+ * in other headers that conflict with this one.
+ */
+#define _GSSAPI_GENERIC_H_
+#define _GSSAPI_KRB5_H_
+
+/*
+ * Define windows specific needed parameters.
+ */
+
+#ifndef GSS_CALLCONV
+#if defined(_WIN32)
+#define GSS_CALLCONV __stdcall
+#define GSS_CALLCONV_C __cdecl
+#else
+#define GSS_CALLCONV
+#define GSS_CALLCONV_C
+#endif
+#endif /* GSS_CALLCONV */
+
+#ifdef GSS_USE_FUNCTION_POINTERS
+#ifdef _WIN32
+#undef GSS_CALLCONV
+#define GSS_CALLCONV
+#define GSS_FUNC(f) (__stdcall *f##_type)
+#else
+#define GSS_FUNC(f) (*f##_type)
+#endif
+#define GSS_MAKE_TYPEDEF typedef
+#else
+#define GSS_FUNC(f) f
+#define GSS_MAKE_TYPEDEF
+#endif
+
+/*
+ * First, include stddef.h to get size_t defined.
+ */
+#include <stddef.h>
+
+/*
+ * Configure set the following
+ */
+
+#ifndef SIZEOF_LONG
+#undef SIZEOF_LONG
+#endif
+#ifndef SIZEOF_SHORT
+#undef SIZEOF_SHORT
+#endif
+
+#ifndef EXTERN_C_BEGIN
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+#endif
+
+EXTERN_C_BEGIN
+
+#if defined(XP_MACOSX)
+# pragma pack(push,2)
+#endif
+
+/*
+ * If the platform supports the xom.h header file, it should be
+ * included here.
+ */
+/* #include <xom.h> */
+
+
+/*
+ * Now define the three implementation-dependent types.
+ */
+
+typedef void * gss_name_t ;
+typedef void * gss_ctx_id_t ;
+typedef void * gss_cred_id_t ;
+
+
+/*
+ * The following type must be defined as the smallest natural
+ * unsigned integer supported by the platform that has at least
+ * 32 bits of precision.
+ */
+
+#if SIZEOF_LONG == 4
+typedef unsigned long gss_uint32;
+#elif SIZEOF_SHORT == 4
+typedef unsigned short gss_uint32;
+#else
+typedef unsigned int gss_uint32;
+#endif
+
+#ifdef OM_STRING
+
+/*
+ * We have included the xom.h header file. Verify that OM_uint32
+ * is defined correctly.
+ */
+
+#if sizeof(gss_uint32) != sizeof(OM_uint32)
+#error Incompatible definition of OM_uint32 from xom.h
+#endif
+
+typedef OM_object_identifier gss_OID_desc, *gss_OID;
+
+#else /* !OM_STRING */
+
+/*
+ * We can't use X/Open definitions, so roll our own.
+ */
+typedef gss_uint32 OM_uint32;
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc, *gss_OID;
+
+#endif /* !OM_STRING */
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc, *gss_OID_set;
+
+
+/*
+ * For now, define a QOP-type as an OM_uint32
+ */
+typedef OM_uint32 gss_qop_t;
+
+typedef int gss_cred_usage_t;
+
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+ OM_uint32 initiator_addrtype;
+ gss_buffer_desc initiator_address;
+ OM_uint32 acceptor_addrtype;
+ gss_buffer_desc acceptor_address;
+ gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG 1
+#define GSS_C_MUTUAL_FLAG 2
+#define GSS_C_REPLAY_FLAG 4
+#define GSS_C_SEQUENCE_FLAG 8
+#define GSS_C_CONF_FLAG 16
+#define GSS_C_INTEG_FLAG 32
+#define GSS_C_ANON_FLAG 64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG 256
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH 0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT 2
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+/*
+ * The constant definitions for channel-bindings address families
+ */
+#define GSS_C_AF_UNSPEC 0
+#define GSS_C_AF_LOCAL 1
+#define GSS_C_AF_INET 2
+#define GSS_C_AF_IMPLINK 3
+#define GSS_C_AF_PUP 4
+#define GSS_C_AF_CHAOS 5
+#define GSS_C_AF_NS 6
+#define GSS_C_AF_NBS 7
+#define GSS_C_AF_ECMA 8
+#define GSS_C_AF_DATAKIT 9
+#define GSS_C_AF_CCITT 10
+#define GSS_C_AF_SNA 11
+#define GSS_C_AF_DECnet 12
+#define GSS_C_AF_DLI 13
+#define GSS_C_AF_LAT 14
+#define GSS_C_AF_HYLINK 15
+#define GSS_C_AF_APPLETALK 16
+#define GSS_C_AF_BSC 17
+#define GSS_C_AF_DSS 18
+#define GSS_C_AF_OSI 19
+#define GSS_C_AF_X25 21
+
+#define GSS_C_AF_NULLADDR 255
+
+/*
+ * Various Null values
+ */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, nullptr}
+
+/*
+ * Some alternate names for a couple of the above
+ * values. These are defined for V1 compatibility.
+ */
+#define GSS_C_NULL_OID GSS_C_NO_OID
+#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
+
+/*
+ * Define the default Quality of Protection for per-message
+ * services. Note that an implementation that offers multiple
+ * levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
+ * (as done here) to mean "default protection", or to a specific
+ * explicit QOP value. However, a value of 0 should always be
+ * interpreted by a GSSAPI implementation as a request for the
+ * default protection level.
+ */
+#define GSS_C_QOP_DEFAULT 0
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE 0xfffffffful
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_USER_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_MACHINE_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_STRING_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)). The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc. This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSSAPI
+ * implementations
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}. The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_ANONYMOUS;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_EXPORT_NAME;
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE 0
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSSAPI so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+(x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+ (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+ (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+ (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+ (1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+ (2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+ (3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+/*
+ * Finally, function prototypes for the GSS-API routines.
+ */
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_acquire_cred)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* desired_name */
+ OM_uint32, /* time_req */
+ const gss_OID_set, /* desired_mechs */
+ gss_cred_usage_t, /* cred_usage */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 * /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_cred)
+(OM_uint32 *, /* minor_status */
+ gss_cred_id_t * /* cred_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_init_sec_context)
+(OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* initiator_cred_handle */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_name_t, /* target_name */
+ const gss_OID, /* mech_type */
+ OM_uint32, /* req_flags */
+ OM_uint32, /* time_req */
+ const gss_channel_bindings_t, /* input_chan_bindings */
+ const gss_buffer_t, /* input_token */
+ gss_OID *, /* actual_mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 * /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_accept_sec_context)
+(OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_cred_id_t, /* acceptor_cred_handle */
+ const gss_buffer_t, /* input_token_buffer */
+ const gss_channel_bindings_t, /* input_chan_bindings */
+ gss_name_t *, /* src_name */
+ gss_OID *, /* mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 *, /* time_rec */
+ gss_cred_id_t * /* delegated_cred_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_process_context_token)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t /* token_buffer */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_delete_sec_context)
+(OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ gss_buffer_t /* output_token */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_context_time)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ OM_uint32 * /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_get_mic)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* message_buffer */
+ gss_buffer_t /* message_token */
+ );
+
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_verify_mic)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* message_buffer */
+ const gss_buffer_t, /* token_buffer */
+ gss_qop_t * /* qop_state */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_wrap)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* input_message_buffer */
+ int *, /* conf_state */
+ gss_buffer_t /* output_message_buffer */
+ );
+
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_unwrap)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* input_message_buffer */
+ gss_buffer_t, /* output_message_buffer */
+ int *, /* conf_state */
+ gss_qop_t * /* qop_state */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_display_status)
+(OM_uint32 *, /* minor_status */
+ OM_uint32, /* status_value */
+ int, /* status_type */
+ const gss_OID, /* mech_type */
+ OM_uint32 *, /* message_context */
+ gss_buffer_t /* status_string */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_indicate_mechs)
+(OM_uint32 *, /* minor_status */
+ gss_OID_set * /* mech_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_compare_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* name1 */
+ const gss_name_t, /* name2 */
+ int * /* name_equal */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_display_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_buffer_t, /* output_name_buffer */
+ gss_OID * /* output_name_type */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_import_name)
+(OM_uint32 *, /* minor_status */
+ const gss_buffer_t, /* input_name_buffer */
+ const gss_OID, /* input_name_type */
+ gss_name_t * /* output_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_export_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_buffer_t /* exported_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_name)
+(OM_uint32 *, /* minor_status */
+ gss_name_t * /* input_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_buffer)
+(OM_uint32 *, /* minor_status */
+ gss_buffer_t /* buffer */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_oid_set)
+(OM_uint32 *, /* minor_status */
+ gss_OID_set * /* set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_cred)
+(OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* cred_handle */
+ gss_name_t *, /* name */
+ OM_uint32 *, /* lifetime */
+ gss_cred_usage_t *, /* cred_usage */
+ gss_OID_set * /* mechanisms */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_context)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_name_t *, /* src_name */
+ gss_name_t *, /* targ_name */
+ OM_uint32 *, /* lifetime_rec */
+ gss_OID *, /* mech_type */
+ OM_uint32 *, /* ctx_flags */
+ int *, /* locally_initiated */
+ int * /* open */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_wrap_size_limit)
+(OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ OM_uint32, /* req_output_size */
+ OM_uint32 * /* max_input_size */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_add_cred)
+(OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* input_cred_handle */
+ const gss_name_t, /* desired_name */
+ const gss_OID, /* desired_mech */
+ gss_cred_usage_t, /* cred_usage */
+ OM_uint32, /* initiator_time_req */
+ OM_uint32, /* acceptor_time_req */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 *, /* initiator_time_rec */
+ OM_uint32 * /* acceptor_time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_cred_by_mech)
+(OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* cred_handle */
+ const gss_OID, /* mech_type */
+ gss_name_t *, /* name */
+ OM_uint32 *, /* initiator_lifetime */
+ OM_uint32 *, /* acceptor_lifetime */
+ gss_cred_usage_t * /* cred_usage */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_export_sec_context)
+(OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ gss_buffer_t /* interprocess_token */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_import_sec_context)
+(OM_uint32 *, /* minor_status */
+ const gss_buffer_t, /* interprocess_token */
+ gss_ctx_id_t * /* context_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_create_empty_oid_set)
+(OM_uint32 *, /* minor_status */
+ gss_OID_set * /* oid_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_add_oid_set_member)
+(OM_uint32 *, /* minor_status */
+ const gss_OID, /* member_oid */
+ gss_OID_set * /* oid_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_test_oid_set_member)
+(OM_uint32 *, /* minor_status */
+ const gss_OID, /* member */
+ const gss_OID_set, /* set */
+ int * /* present */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_names_for_mech)
+(OM_uint32 *, /* minor_status */
+ const gss_OID, /* mechanism */
+ gss_OID_set * /* name_types */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_mechs_for_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_OID_set * /* mech_types */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_canonicalize_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ const gss_OID, /* mech_type */
+ gss_name_t * /* output_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_duplicate_name)
+(OM_uint32 *, /* minor_status */
+ const gss_name_t, /* src_name */
+ gss_name_t * /* dest_name */
+ );
+
+ /*
+ * The following routines are obsolete variants of gss_get_mic,
+ * gss_verify_mic, gss_wrap and gss_unwrap. They should be
+ * provided by GSSAPI V2 implementations for backwards
+ * compatibility with V1 applications. Distinct entrypoints
+ * (as opposed to #defines) should be provided, both to allow
+ * GSSAPI V1 applications to link against GSSAPI V2 implementations,
+ * and to retain the slight parameter type differences between the
+ * obsolete versions of these routines and their current forms.
+ */
+
+ GSS_MAKE_TYPEDEF
+ OM_uint32
+ GSS_CALLCONV GSS_FUNC(gss_sign)
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t, /* context_handle */
+ int, /* qop_req */
+ gss_buffer_t, /* message_buffer */
+ gss_buffer_t /* message_token */
+ );
+
+
+ GSS_MAKE_TYPEDEF
+ OM_uint32
+ GSS_CALLCONV GSS_FUNC(gss_verify)
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t, /* context_handle */
+ gss_buffer_t, /* message_buffer */
+ gss_buffer_t, /* token_buffer */
+ int * /* qop_state */
+ );
+
+ GSS_MAKE_TYPEDEF
+ OM_uint32
+ GSS_CALLCONV GSS_FUNC(gss_seal)
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ int, /* qop_req */
+ gss_buffer_t, /* input_message_buffer */
+ int *, /* conf_state */
+ gss_buffer_t /* output_message_buffer */
+ );
+
+
+ GSS_MAKE_TYPEDEF
+ OM_uint32
+ GSS_CALLCONV GSS_FUNC(gss_unseal)
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t, /* context_handle */
+ gss_buffer_t, /* input_message_buffer */
+ gss_buffer_t, /* output_message_buffer */
+ int *, /* conf_state */
+ int * /* qop_state */
+ );
+
+
+#if defined(XP_MACOSX)
+# pragma pack(pop)
+#endif
+
+EXTERN_C_END
+
+#endif /* GSSAPI_H_ */
+
diff --git a/extensions/auth/moz.build b/extensions/auth/moz.build
new file mode 100644
index 000000000..a925fdad0
--- /dev/null
+++ b/extensions/auth/moz.build
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'nsAuthFactory.cpp',
+ 'nsAuthGSSAPI.cpp',
+]
+
+SOURCES += [
+ 'nsAuthSASL.cpp',
+ 'nsHttpNegotiateAuth.cpp', # contains constants whose names conflict with constants in other files
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += [
+ 'nsAuthSSPI.cpp',
+ ]
+ DEFINES['USE_SSPI'] = True
+else:
+ UNIFIED_SOURCES += [
+ 'nsAuthSambaNTLM.cpp',
+ ]
+
+FINAL_LIBRARY = 'xul'
diff --git a/extensions/auth/nsAuth.h b/extensions/auth/nsAuth.h
new file mode 100644
index 000000000..a32072cdb
--- /dev/null
+++ b/extensions/auth/nsAuth.h
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAuth_h__
+#define nsAuth_h__
+
+/* types of packages */
+enum pType {
+ PACKAGE_TYPE_KERBEROS,
+ PACKAGE_TYPE_NEGOTIATE,
+ PACKAGE_TYPE_NTLM
+};
+
+#include "mozilla/Logging.h"
+
+//
+// in order to do logging, the following environment variables need to be set:
+//
+// set NSPR_LOG_MODULES=negotiateauth:4
+// set NSPR_LOG_FILE=negotiateauth.log
+//
+extern mozilla::LazyLogModule gNegotiateLog;
+
+#define LOG(args) MOZ_LOG(gNegotiateLog, mozilla::LogLevel::Debug, args)
+
+#endif /* !defined( nsAuth_h__ ) */
diff --git a/extensions/auth/nsAuthFactory.cpp b/extensions/auth/nsAuthFactory.cpp
new file mode 100644
index 000000000..d026a900b
--- /dev/null
+++ b/extensions/auth/nsAuthFactory.cpp
@@ -0,0 +1,247 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsAuth.h"
+
+//-----------------------------------------------------------------------------
+
+#define NS_HTTPNEGOTIATEAUTH_CID \
+{ /* 75c80fd0-accb-432c-af59-ec60668c3990 */ \
+ 0x75c80fd0, \
+ 0xaccb, \
+ 0x432c, \
+ {0xaf, 0x59, 0xec, 0x60, 0x66, 0x8c, 0x39, 0x90} \
+}
+
+#include "nsHttpNegotiateAuth.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNegotiateAuth)
+//-----------------------------------------------------------------------------
+
+#define NS_NEGOTIATEAUTH_CID \
+{ /* 96ec4163-efc8-407a-8735-007fb26be4e8 */ \
+ 0x96ec4163, \
+ 0xefc8, \
+ 0x407a, \
+ {0x87, 0x35, 0x00, 0x7f, 0xb2, 0x6b, 0xe4, 0xe8} \
+}
+#define NS_GSSAUTH_CID \
+{ /* dc8e21a0-03e4-11da-8cd6-0800200c9a66 */ \
+ 0xdc8e21a0, \
+ 0x03e4, \
+ 0x11da, \
+ {0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
+}
+
+#include "nsAuthGSSAPI.h"
+
+#if defined( USE_SSPI )
+#include "nsAuthSSPI.h"
+
+static nsresult
+nsSysNTLMAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsAuthSSPI *auth = new nsAuthSSPI(PACKAGE_TYPE_NTLM);
+ if (!auth)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(auth);
+ nsresult rv = auth->QueryInterface(iid, result);
+ NS_RELEASE(auth);
+ return rv;
+}
+
+static nsresult
+nsKerbSSPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsAuthSSPI *auth = new nsAuthSSPI(PACKAGE_TYPE_KERBEROS);
+ if (!auth)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(auth);
+ nsresult rv = auth->QueryInterface(iid, result);
+ NS_RELEASE(auth);
+ return rv;
+}
+
+#define NS_SYSNTLMAUTH_CID \
+{ /* dc195987-6e9a-47bc-b1fd-ab895d398833 */ \
+ 0xdc195987, \
+ 0x6e9a, \
+ 0x47bc, \
+ {0xb1, 0xfd, 0xab, 0x89, 0x5d, 0x39, 0x88, 0x33} \
+}
+
+#define NS_NEGOTIATEAUTHSSPI_CID \
+{ /* 78d3b0c0-0241-11da-8cd6-0800200c9a66 */ \
+ 0x78d3b0c0, \
+ 0x0241, \
+ 0x11da, \
+ {0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
+}
+
+#define NS_KERBAUTHSSPI_CID \
+{ /* 8c3a0e20-03e5-11da-8cd6-0800200c9a66 */ \
+ 0x8c3a0e20, \
+ 0x03e5, \
+ 0x11da, \
+ {0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
+}
+
+#else
+
+#define NS_SAMBANTLMAUTH_CID \
+{ /* bc54f001-6eb0-4e32-9f49-7e064d8e70ef */ \
+ 0xbc54f001, \
+ 0x6eb0, \
+ 0x4e32, \
+ {0x9f, 0x49, 0x7e, 0x06, 0x4d, 0x8e, 0x70, 0xef} \
+}
+
+#include "nsAuthSambaNTLM.h"
+static nsresult
+nsSambaNTLMAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ RefPtr<nsAuthSambaNTLM> auth = new nsAuthSambaNTLM();
+ if (!auth)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = auth->SpawnNTLMAuthHelper();
+ if (NS_FAILED(rv)) {
+ // Failure here probably means that cached credentials were not available
+ return rv;
+ }
+
+ return auth->QueryInterface(iid, result);
+}
+
+#endif
+
+static nsresult
+nsKerbGSSAPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsAuthGSSAPI *auth = new nsAuthGSSAPI(PACKAGE_TYPE_KERBEROS);
+ if (!auth)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(auth);
+ nsresult rv = auth->QueryInterface(iid, result);
+ NS_RELEASE(auth);
+ return rv;
+}
+
+static nsresult
+nsGSSAPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsAuthGSSAPI *auth = new nsAuthGSSAPI(PACKAGE_TYPE_NEGOTIATE);
+ if (!auth)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(auth);
+ nsresult rv = auth->QueryInterface(iid, result);
+ NS_RELEASE(auth);
+ return rv;
+}
+
+
+#if defined( USE_SSPI )
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAuthSSPI)
+#endif
+
+#define NS_AUTHSASL_CID \
+{ /* 815e42e0-72cc-480f-934b-148e33c228a6 */ \
+ 0x815e42e0, \
+ 0x72cc, \
+ 0x480f, \
+ {0x93, 0x4b, 0x14, 0x8e, 0x33, 0xc2, 0x28, 0xa6} \
+}
+
+#include "nsAuthSASL.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAuthSASL)
+
+NS_DEFINE_NAMED_CID(NS_GSSAUTH_CID);
+NS_DEFINE_NAMED_CID(NS_NEGOTIATEAUTH_CID);
+#if defined( USE_SSPI )
+NS_DEFINE_NAMED_CID(NS_NEGOTIATEAUTHSSPI_CID);
+NS_DEFINE_NAMED_CID(NS_KERBAUTHSSPI_CID);
+NS_DEFINE_NAMED_CID(NS_SYSNTLMAUTH_CID);
+#else
+NS_DEFINE_NAMED_CID(NS_SAMBANTLMAUTH_CID);
+#endif
+NS_DEFINE_NAMED_CID(NS_HTTPNEGOTIATEAUTH_CID);
+NS_DEFINE_NAMED_CID(NS_AUTHSASL_CID);
+
+
+static const mozilla::Module::CIDEntry kAuthCIDs[] = {
+ { &kNS_GSSAUTH_CID, false, nullptr, nsKerbGSSAPIAuthConstructor },
+ { &kNS_NEGOTIATEAUTH_CID, false, nullptr, nsGSSAPIAuthConstructor },
+#if defined( USE_SSPI )
+ { &kNS_NEGOTIATEAUTHSSPI_CID, false, nullptr, nsAuthSSPIConstructor },
+ { &kNS_KERBAUTHSSPI_CID, false, nullptr, nsKerbSSPIAuthConstructor },
+ { &kNS_SYSNTLMAUTH_CID, false, nullptr, nsSysNTLMAuthConstructor },
+#else
+ { &kNS_SAMBANTLMAUTH_CID, false, nullptr, nsSambaNTLMAuthConstructor },
+#endif
+ { &kNS_HTTPNEGOTIATEAUTH_CID, false, nullptr, nsHttpNegotiateAuthConstructor },
+ { &kNS_AUTHSASL_CID, false, nullptr, nsAuthSASLConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kAuthContracts[] = {
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-gss", &kNS_GSSAUTH_CID },
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss", &kNS_NEGOTIATEAUTH_CID },
+#if defined( USE_SSPI )
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi", &kNS_NEGOTIATEAUTHSSPI_CID },
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-sspi", &kNS_KERBAUTHSSPI_CID },
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", &kNS_SYSNTLMAUTH_CID },
+#else
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", &kNS_SAMBANTLMAUTH_CID },
+#endif
+ { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "negotiate", &kNS_HTTPNEGOTIATEAUTH_CID },
+ { NS_AUTH_MODULE_CONTRACTID_PREFIX "sasl-gssapi", &kNS_AUTHSASL_CID },
+ { nullptr }
+};
+
+//-----------------------------------------------------------------------------
+mozilla::LazyLogModule gNegotiateLog("negotiateauth");
+
+// setup nspr logging ...
+static nsresult
+InitNegotiateAuth()
+{
+ return NS_OK;
+}
+
+static void
+DestroyNegotiateAuth()
+{
+ nsAuthGSSAPI::Shutdown();
+}
+
+static const mozilla::Module kAuthModule = {
+ mozilla::Module::kVersion,
+ kAuthCIDs,
+ kAuthContracts,
+ nullptr,
+ nullptr,
+ InitNegotiateAuth,
+ DestroyNegotiateAuth
+};
+
+NSMODULE_DEFN(nsAuthModule) = &kAuthModule;
diff --git a/extensions/auth/nsAuthGSSAPI.cpp b/extensions/auth/nsAuthGSSAPI.cpp
new file mode 100644
index 000000000..f63b30eff
--- /dev/null
+++ b/extensions/auth/nsAuthGSSAPI.cpp
@@ -0,0 +1,603 @@
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// GSSAPI Authentication Support Module
+//
+// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
+// (formerly draft-brezak-spnego-http-04.txt)
+//
+// Also described here:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
+//
+//
+
+#include "mozilla/ArrayUtils.h"
+
+#include "prlink.h"
+#include "nsCOMPtr.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIServiceManager.h"
+#include "nsNativeCharsetUtils.h"
+#include "mozilla/Telemetry.h"
+
+#include "nsAuthGSSAPI.h"
+
+#ifdef XP_MACOSX
+#include <Kerberos/Kerberos.h>
+#endif
+
+#ifdef XP_MACOSX
+typedef KLStatus (*KLCacheHasValidTickets_type)(
+ KLPrincipal,
+ KLKerberosVersion,
+ KLBoolean *,
+ KLPrincipal *,
+ char **);
+#endif
+
+#if defined(HAVE_RES_NINIT)
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+using namespace mozilla;
+
+//-----------------------------------------------------------------------------
+
+// We define GSS_C_NT_HOSTBASED_SERVICE explicitly since it may be referenced
+// by by a different name depending on the implementation of gss but always
+// has the same value
+
+static gss_OID_desc gss_c_nt_hostbased_service =
+ { 10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04" };
+
+static const char kNegotiateAuthGssLib[] =
+ "network.negotiate-auth.gsslib";
+static const char kNegotiateAuthNativeImp[] =
+ "network.negotiate-auth.using-native-gsslib";
+
+static struct GSSFunction {
+ const char *str;
+ PRFuncPtr func;
+} gssFuncs[] = {
+ { "gss_display_status", nullptr },
+ { "gss_init_sec_context", nullptr },
+ { "gss_indicate_mechs", nullptr },
+ { "gss_release_oid_set", nullptr },
+ { "gss_delete_sec_context", nullptr },
+ { "gss_import_name", nullptr },
+ { "gss_release_buffer", nullptr },
+ { "gss_release_name", nullptr },
+ { "gss_wrap", nullptr },
+ { "gss_unwrap", nullptr }
+};
+
+static bool gssNativeImp = true;
+static PRLibrary* gssLibrary = nullptr;
+
+#define gss_display_status_ptr ((gss_display_status_type)*gssFuncs[0].func)
+#define gss_init_sec_context_ptr ((gss_init_sec_context_type)*gssFuncs[1].func)
+#define gss_indicate_mechs_ptr ((gss_indicate_mechs_type)*gssFuncs[2].func)
+#define gss_release_oid_set_ptr ((gss_release_oid_set_type)*gssFuncs[3].func)
+#define gss_delete_sec_context_ptr ((gss_delete_sec_context_type)*gssFuncs[4].func)
+#define gss_import_name_ptr ((gss_import_name_type)*gssFuncs[5].func)
+#define gss_release_buffer_ptr ((gss_release_buffer_type)*gssFuncs[6].func)
+#define gss_release_name_ptr ((gss_release_name_type)*gssFuncs[7].func)
+#define gss_wrap_ptr ((gss_wrap_type)*gssFuncs[8].func)
+#define gss_unwrap_ptr ((gss_unwrap_type)*gssFuncs[9].func)
+
+#ifdef XP_MACOSX
+static PRFuncPtr KLCacheHasValidTicketsPtr;
+#define KLCacheHasValidTickets_ptr \
+ ((KLCacheHasValidTickets_type)*KLCacheHasValidTicketsPtr)
+#endif
+
+static nsresult
+gssInit()
+{
+ nsXPIDLCString libPath;
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ prefs->GetCharPref(kNegotiateAuthGssLib, getter_Copies(libPath));
+ prefs->GetBoolPref(kNegotiateAuthNativeImp, &gssNativeImp);
+ }
+
+ PRLibrary *lib = nullptr;
+
+ if (!libPath.IsEmpty()) {
+ LOG(("Attempting to load user specified library [%s]\n", libPath.get()));
+ gssNativeImp = false;
+ lib = PR_LoadLibrary(libPath.get());
+ }
+ else {
+#ifdef XP_WIN
+ char *libName = PR_GetLibraryName(nullptr, "gssapi32");
+ if (libName) {
+ lib = PR_LoadLibrary("gssapi32");
+ PR_FreeLibraryName(libName);
+ }
+#elif defined(__OpenBSD__)
+ /* OpenBSD doesn't register inter-library dependencies in basesystem
+ * libs therefor we need to load all the libraries gssapi depends on,
+ * in the correct order and with LD_GLOBAL for GSSAPI auth to work
+ * fine.
+ */
+
+ const char *const verLibNames[] = {
+ "libasn1.so",
+ "libcrypto.so",
+ "libroken.so",
+ "libheimbase.so",
+ "libcom_err.so",
+ "libkrb5.so",
+ "libgssapi.so"
+ };
+
+ PRLibSpec libSpec;
+ for (size_t i = 0; i < ArrayLength(verLibNames); ++i) {
+ libSpec.type = PR_LibSpec_Pathname;
+ libSpec.value.pathname = verLibNames[i];
+ lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_GLOBAL);
+ }
+
+#else
+
+ const char *const libNames[] = {
+ "gss",
+ "gssapi_krb5",
+ "gssapi"
+ };
+
+ const char *const verLibNames[] = {
+ "libgssapi_krb5.so.2", /* MIT - FC, Suse10, Debian */
+ "libgssapi.so.4", /* Heimdal - Suse10, MDK */
+ "libgssapi.so.1" /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/
+ };
+
+ for (size_t i = 0; i < ArrayLength(verLibNames) && !lib; ++i) {
+ lib = PR_LoadLibrary(verLibNames[i]);
+
+ /* The CITI libgssapi library calls exit() during
+ * initialization if it's not correctly configured. Try to
+ * ensure that we never use this library for our GSSAPI
+ * support, as its just a wrapper library, anyway.
+ * See Bugzilla #325433
+ */
+ if (lib &&
+ PR_FindFunctionSymbol(lib,
+ "internal_krb5_gss_initialize") &&
+ PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
+ LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
+ PR_UnloadLibrary(lib);
+ lib = nullptr;
+ }
+ }
+
+ for (size_t i = 0; i < ArrayLength(libNames) && !lib; ++i) {
+ char *libName = PR_GetLibraryName(nullptr, libNames[i]);
+ if (libName) {
+ lib = PR_LoadLibrary(libName);
+ PR_FreeLibraryName(libName);
+
+ if (lib &&
+ PR_FindFunctionSymbol(lib,
+ "internal_krb5_gss_initialize") &&
+ PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
+ LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
+ PR_UnloadLibrary(lib);
+ lib = nullptr;
+ }
+ }
+ }
+#endif
+ }
+
+ if (!lib) {
+ LOG(("Fail to load gssapi library\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG(("Attempting to load gss functions\n"));
+
+ for (size_t i = 0; i < ArrayLength(gssFuncs); ++i) {
+ gssFuncs[i].func = PR_FindFunctionSymbol(lib, gssFuncs[i].str);
+ if (!gssFuncs[i].func) {
+ LOG(("Fail to load %s function from gssapi library\n", gssFuncs[i].str));
+ PR_UnloadLibrary(lib);
+ return NS_ERROR_FAILURE;
+ }
+ }
+#ifdef XP_MACOSX
+ if (gssNativeImp &&
+ !(KLCacheHasValidTicketsPtr =
+ PR_FindFunctionSymbol(lib, "KLCacheHasValidTickets"))) {
+ LOG(("Fail to load KLCacheHasValidTickets function from gssapi library\n"));
+ PR_UnloadLibrary(lib);
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ gssLibrary = lib;
+ return NS_OK;
+}
+
+// Generate proper GSSAPI error messages from the major and
+// minor status codes.
+void
+LogGssError(OM_uint32 maj_stat, OM_uint32 min_stat, const char *prefix)
+{
+ if (!MOZ_LOG_TEST(gNegotiateLog, LogLevel::Debug)) {
+ return;
+ }
+
+ OM_uint32 new_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status1_string;
+ gss_buffer_desc status2_string;
+ OM_uint32 ret;
+ nsAutoCString errorStr;
+ errorStr.Assign(prefix);
+
+ if (!gssLibrary)
+ return;
+
+ errorStr += ": ";
+ do {
+ ret = gss_display_status_ptr(&new_stat,
+ maj_stat,
+ GSS_C_GSS_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx,
+ &status1_string);
+ errorStr.Append((const char *) status1_string.value, status1_string.length);
+ gss_release_buffer_ptr(&new_stat, &status1_string);
+
+ errorStr += '\n';
+ ret = gss_display_status_ptr(&new_stat,
+ min_stat,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx,
+ &status2_string);
+ errorStr.Append((const char *) status2_string.value, status2_string.length);
+ errorStr += '\n';
+ } while (!GSS_ERROR(ret) && msg_ctx != 0);
+
+ LOG(("%s\n", errorStr.get()));
+}
+
+//-----------------------------------------------------------------------------
+
+nsAuthGSSAPI::nsAuthGSSAPI(pType package)
+ : mServiceFlags(REQ_DEFAULT)
+{
+ OM_uint32 minstat;
+ OM_uint32 majstat;
+ gss_OID_set mech_set;
+ gss_OID item;
+
+ unsigned int i;
+ static gss_OID_desc gss_krb5_mech_oid_desc =
+ { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+ static gss_OID_desc gss_spnego_mech_oid_desc =
+ { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+
+ LOG(("entering nsAuthGSSAPI::nsAuthGSSAPI()\n"));
+
+ mComplete = false;
+
+ if (!gssLibrary && NS_FAILED(gssInit()))
+ return;
+
+ mCtx = GSS_C_NO_CONTEXT;
+ mMechOID = &gss_krb5_mech_oid_desc;
+
+ // if the type is kerberos we accept it as default
+ // and exit
+
+ if (package == PACKAGE_TYPE_KERBEROS)
+ return;
+
+ // Now, look at the list of supported mechanisms,
+ // if SPNEGO is found, then use it.
+ // Otherwise, set the desired mechanism to
+ // GSS_C_NO_OID and let the system try to use
+ // the default mechanism.
+ //
+ // Using Kerberos directly (instead of negotiating
+ // with SPNEGO) may work in some cases depending
+ // on how smart the server side is.
+
+ majstat = gss_indicate_mechs_ptr(&minstat, &mech_set);
+ if (GSS_ERROR(majstat))
+ return;
+
+ if (mech_set) {
+ for (i=0; i<mech_set->count; i++) {
+ item = &mech_set->elements[i];
+ if (item->length == gss_spnego_mech_oid_desc.length &&
+ !memcmp(item->elements, gss_spnego_mech_oid_desc.elements,
+ item->length)) {
+ // ok, we found it
+ mMechOID = &gss_spnego_mech_oid_desc;
+ break;
+ }
+ }
+ gss_release_oid_set_ptr(&minstat, &mech_set);
+ }
+}
+
+void
+nsAuthGSSAPI::Reset()
+{
+ if (gssLibrary && mCtx != GSS_C_NO_CONTEXT) {
+ OM_uint32 minor_status;
+ gss_delete_sec_context_ptr(&minor_status, &mCtx, GSS_C_NO_BUFFER);
+ }
+ mCtx = GSS_C_NO_CONTEXT;
+ mComplete = false;
+}
+
+/* static */ void
+nsAuthGSSAPI::Shutdown()
+{
+ if (gssLibrary) {
+ PR_UnloadLibrary(gssLibrary);
+ gssLibrary = nullptr;
+ }
+}
+
+/* Limitations apply to this class's thread safety. See the header file */
+NS_IMPL_ISUPPORTS(nsAuthGSSAPI, nsIAuthModule)
+
+NS_IMETHODIMP
+nsAuthGSSAPI::Init(const char *serviceName,
+ uint32_t serviceFlags,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password)
+{
+ // we don't expect to be passed any user credentials
+ NS_ASSERTION(!domain && !username && !password, "unexpected credentials");
+
+ // it's critial that the caller supply a service name to be used
+ NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
+
+ LOG(("entering nsAuthGSSAPI::Init()\n"));
+
+ if (!gssLibrary)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ mServiceName = serviceName;
+ mServiceFlags = serviceFlags;
+
+ static bool sTelemetrySent = false;
+ if (!sTelemetrySent) {
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::NTLM_MODULE_USED_2,
+ serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
+ ? NTLM_MODULE_KERBEROS_PROXY
+ : NTLM_MODULE_KERBEROS_DIRECT);
+ sTelemetrySent = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAuthGSSAPI::GetNextToken(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ OM_uint32 major_status, minor_status;
+ OM_uint32 req_flags = 0;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_t in_token_ptr = GSS_C_NO_BUFFER;
+ gss_name_t server;
+ nsAutoCString userbuf;
+ nsresult rv;
+
+ LOG(("entering nsAuthGSSAPI::GetNextToken()\n"));
+
+ if (!gssLibrary)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // If they've called us again after we're complete, reset to start afresh.
+ if (mComplete)
+ Reset();
+
+ if (mServiceFlags & REQ_DELEGATE)
+ req_flags |= GSS_C_DELEG_FLAG;
+
+ if (mServiceFlags & REQ_MUTUAL_AUTH)
+ req_flags |= GSS_C_MUTUAL_FLAG;
+
+ input_token.value = (void *)mServiceName.get();
+ input_token.length = mServiceName.Length() + 1;
+
+#if defined(HAVE_RES_NINIT)
+ res_ninit(&_res);
+#endif
+ major_status = gss_import_name_ptr(&minor_status,
+ &input_token,
+ &gss_c_nt_hostbased_service,
+ &server);
+ input_token.value = nullptr;
+ input_token.length = 0;
+ if (GSS_ERROR(major_status)) {
+ LogGssError(major_status, minor_status, "gss_import_name() failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (inToken) {
+ input_token.length = inTokenLen;
+ input_token.value = (void *) inToken;
+ in_token_ptr = &input_token;
+ }
+ else if (mCtx != GSS_C_NO_CONTEXT) {
+ // If there is no input token, then we are starting a new
+ // authentication sequence. If we have already initialized our
+ // security context, then we're in trouble because it means that the
+ // first sequence failed. We need to bail or else we might end up in
+ // an infinite loop.
+ LOG(("Cannot restart authentication sequence!"));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+#if defined(XP_MACOSX)
+ // Suppress Kerberos prompts to get credentials. See bug 240643.
+ // We can only use Mac OS X specific kerb functions if we are using
+ // the native lib
+ KLBoolean found;
+ bool doingMailTask = mServiceName.Find("imap@") ||
+ mServiceName.Find("pop@") ||
+ mServiceName.Find("smtp@") ||
+ mServiceName.Find("ldap@");
+
+ if (!doingMailTask && (gssNativeImp &&
+ (KLCacheHasValidTickets_ptr(nullptr, kerberosVersion_V5, &found, nullptr, nullptr) != klNoErr || !found)))
+ {
+ major_status = GSS_S_FAILURE;
+ minor_status = 0;
+ }
+ else
+#endif /* XP_MACOSX */
+ major_status = gss_init_sec_context_ptr(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &mCtx,
+ server,
+ mMechOID,
+ req_flags,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ in_token_ptr,
+ nullptr,
+ &output_token,
+ nullptr,
+ nullptr);
+
+ if (GSS_ERROR(major_status)) {
+ LogGssError(major_status, minor_status, "gss_init_sec_context() failed");
+ Reset();
+ rv = NS_ERROR_FAILURE;
+ goto end;
+ }
+ if (major_status == GSS_S_COMPLETE) {
+ // Mark ourselves as being complete, so that if we're called again
+ // we know to start afresh.
+ mComplete = true;
+ }
+ else if (major_status == GSS_S_CONTINUE_NEEDED) {
+ //
+ // The important thing is that we do NOT reset the
+ // context here because it will be needed on the
+ // next call.
+ //
+ }
+
+ *outTokenLen = output_token.length;
+ if (output_token.length != 0)
+ *outToken = nsMemory::Clone(output_token.value, output_token.length);
+ else
+ *outToken = nullptr;
+
+ gss_release_buffer_ptr(&minor_status, &output_token);
+
+ if (major_status == GSS_S_COMPLETE)
+ rv = NS_SUCCESS_AUTH_FINISHED;
+ else
+ rv = NS_OK;
+
+end:
+ gss_release_name_ptr(&minor_status, &server);
+
+ LOG((" leaving nsAuthGSSAPI::GetNextToken [rv=%x]", rv));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsAuthGSSAPI::Unwrap(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ OM_uint32 major_status, minor_status;
+
+ gss_buffer_desc input_token;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+
+ input_token.value = (void *) inToken;
+ input_token.length = inTokenLen;
+
+ major_status = gss_unwrap_ptr(&minor_status,
+ mCtx,
+ &input_token,
+ &output_token,
+ nullptr,
+ nullptr);
+ if (GSS_ERROR(major_status)) {
+ LogGssError(major_status, minor_status, "gss_unwrap() failed");
+ Reset();
+ gss_release_buffer_ptr(&minor_status, &output_token);
+ return NS_ERROR_FAILURE;
+ }
+
+ *outTokenLen = output_token.length;
+
+ if (output_token.length)
+ *outToken = nsMemory::Clone(output_token.value, output_token.length);
+ else
+ *outToken = nullptr;
+
+ gss_release_buffer_ptr(&minor_status, &output_token);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAuthGSSAPI::Wrap(const void *inToken,
+ uint32_t inTokenLen,
+ bool confidential,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ OM_uint32 major_status, minor_status;
+
+ gss_buffer_desc input_token;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+
+ input_token.value = (void *) inToken;
+ input_token.length = inTokenLen;
+
+ major_status = gss_wrap_ptr(&minor_status,
+ mCtx,
+ confidential,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ nullptr,
+ &output_token);
+
+ if (GSS_ERROR(major_status)) {
+ LogGssError(major_status, minor_status, "gss_wrap() failed");
+ Reset();
+ gss_release_buffer_ptr(&minor_status, &output_token);
+ return NS_ERROR_FAILURE;
+ }
+
+ *outTokenLen = output_token.length;
+
+ /* it is not possible for output_token.length to be zero */
+ *outToken = nsMemory::Clone(output_token.value, output_token.length);
+ gss_release_buffer_ptr(&minor_status, &output_token);
+
+ return NS_OK;
+}
+
diff --git a/extensions/auth/nsAuthGSSAPI.h b/extensions/auth/nsAuthGSSAPI.h
new file mode 100644
index 000000000..6c663e935
--- /dev/null
+++ b/extensions/auth/nsAuthGSSAPI.h
@@ -0,0 +1,63 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAuthGSSAPI_h__
+#define nsAuthGSSAPI_h__
+
+#include "nsAuth.h"
+#include "nsIAuthModule.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+#define GSS_USE_FUNCTION_POINTERS 1
+
+#include "gssapi.h"
+
+// The nsAuthGSSAPI class provides responses for the GSS-API Negotiate method
+// as specified by Microsoft in draft-brezak-spnego-http-04.txt
+
+/* Some remarks on thread safety ...
+ *
+ * The thread safety of this class depends largely upon the thread safety of
+ * the underlying GSSAPI and Kerberos libraries. This code just loads the
+ * system GSSAPI library, and whilst it avoids loading known bad libraries,
+ * it cannot determine the thread safety of the the code it loads.
+ *
+ * When used with a non-threadsafe library, it is not safe to simultaneously
+ * use multiple instantiations of this class.
+ *
+ * When used with a threadsafe Kerberos library, multiple instantiations of
+ * this class may happily co-exist. Methods may be sequentially called from
+ * multiple threads. The nature of the GSSAPI protocol is such that a correct
+ * implementation will never call methods in parallel, as the results of the
+ * last call are required as input to the next.
+ */
+
+class nsAuthGSSAPI final : public nsIAuthModule
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIAUTHMODULE
+
+ explicit nsAuthGSSAPI(pType package);
+
+ static void Shutdown();
+
+private:
+ ~nsAuthGSSAPI() { Reset(); }
+
+ void Reset();
+ gss_OID GetOID() { return mMechOID; }
+
+private:
+ gss_ctx_id_t mCtx;
+ gss_OID mMechOID;
+ nsCString mServiceName;
+ uint32_t mServiceFlags;
+ nsString mUsername;
+ bool mComplete;
+};
+
+#endif /* nsAuthGSSAPI_h__ */
diff --git a/extensions/auth/nsAuthSASL.cpp b/extensions/auth/nsAuthSASL.cpp
new file mode 100644
index 000000000..2c72cb0ac
--- /dev/null
+++ b/extensions/auth/nsAuthSASL.cpp
@@ -0,0 +1,151 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsComponentManagerUtils.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIPrefService.h"
+
+#include "nsAuthSASL.h"
+
+static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
+
+nsAuthSASL::nsAuthSASL()
+{
+ mSASLReady = false;
+}
+
+void nsAuthSASL::Reset()
+{
+ mSASLReady = false;
+}
+
+/* Limitations apply to this class's thread safety. See the header file */
+NS_IMPL_ISUPPORTS(nsAuthSASL, nsIAuthModule)
+
+NS_IMETHODIMP
+nsAuthSASL::Init(const char *serviceName,
+ uint32_t serviceFlags,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password)
+{
+ nsresult rv;
+
+ NS_ASSERTION(username, "SASL requires a username");
+ NS_ASSERTION(!domain && !password, "unexpected credentials");
+
+ mUsername = username;
+
+ // If we're doing SASL, we should do mutual auth
+ serviceFlags |= REQ_MUTUAL_AUTH;
+
+ // Find out whether we should be trying SSPI or not
+ const char *contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-gss";
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ bool val;
+ rv = prefs->GetBoolPref(kNegotiateAuthSSPI, &val);
+ if (NS_SUCCEEDED(rv) && val)
+ contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-sspi";
+ }
+
+ mInnerModule = do_CreateInstance(contractID, &rv);
+ // if we can't create the GSSAPI module, then bail
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInnerModule->Init(serviceName, serviceFlags, nullptr, nullptr, nullptr);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAuthSASL::GetNextToken(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ nsresult rv;
+ void *unwrappedToken;
+ char *message;
+ uint32_t unwrappedTokenLen, messageLen;
+ nsAutoCString userbuf;
+
+ if (!mInnerModule)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mSASLReady) {
+ // If the server COMPLETEs with an empty token, Cyrus sends us that token.
+ // I don't think this is correct, but we need to handle that behaviour.
+ // Cyrus ignores the contents of our reply token.
+ if (inTokenLen == 0) {
+ *outToken = nullptr;
+ *outTokenLen = 0;
+ return NS_OK;
+ }
+ // We've completed the GSSAPI portion of the handshake, and are
+ // now ready to do the SASL security layer and authzid negotiation
+
+ // Input packet from the server needs to be unwrapped.
+ rv = mInnerModule->Unwrap(inToken, inTokenLen, &unwrappedToken,
+ &unwrappedTokenLen);
+ if (NS_FAILED(rv)) {
+ Reset();
+ return rv;
+ }
+
+ // If we were doing security layers then we'd care what the
+ // server had sent us. We're not, so all we had to do was make
+ // sure that the signature was correct with the above unwrap()
+ free(unwrappedToken);
+
+ NS_CopyUnicodeToNative(mUsername, userbuf);
+ messageLen = userbuf.Length() + 4 + 1;
+ message = (char *)moz_xmalloc(messageLen);
+ if (!message) {
+ Reset();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ message[0] = 0x01; // No security layer
+ message[1] = 0x00;
+ message[2] = 0x00;
+ message[3] = 0x00; // Maxbuf must be zero if we've got no sec layer
+ strcpy(message+4, userbuf.get());
+ // Userbuf should not be nullptr terminated, so trim the trailing nullptr
+ // when wrapping the message
+ rv = mInnerModule->Wrap((void *) message, messageLen-1, false,
+ outToken, outTokenLen);
+ free(message);
+ Reset(); // All done
+ return NS_SUCCEEDED(rv) ? NS_SUCCESS_AUTH_FINISHED : rv;
+ }
+ rv = mInnerModule->GetNextToken(inToken, inTokenLen, outToken,
+ outTokenLen);
+ if (rv == NS_SUCCESS_AUTH_FINISHED) {
+ mSASLReady = true;
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsAuthSASL::Unwrap(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsAuthSASL::Wrap(const void *inToken,
+ uint32_t inTokenLen,
+ bool confidential,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/extensions/auth/nsAuthSASL.h b/extensions/auth/nsAuthSASL.h
new file mode 100644
index 000000000..0cbd79a98
--- /dev/null
+++ b/extensions/auth/nsAuthSASL.h
@@ -0,0 +1,38 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAuthSASL_h__
+#define nsAuthSASL_h__
+
+#include "nsIAuthModule.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+/* This class is implemented using the nsAuthGSSAPI class, and the same
+ * thread safety constraints which are documented in nsAuthGSSAPI.h
+ * apply to this class
+ */
+
+class nsAuthSASL final : public nsIAuthModule
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIAUTHMODULE
+
+ nsAuthSASL();
+
+private:
+ ~nsAuthSASL() { Reset(); }
+
+ void Reset();
+
+ nsCOMPtr<nsIAuthModule> mInnerModule;
+ nsString mUsername;
+ bool mSASLReady;
+};
+
+#endif /* nsAuthSASL_h__ */
+
diff --git a/extensions/auth/nsAuthSSPI.cpp b/extensions/auth/nsAuthSSPI.cpp
new file mode 100644
index 000000000..eb577d3bf
--- /dev/null
+++ b/extensions/auth/nsAuthSSPI.cpp
@@ -0,0 +1,666 @@
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Negotiate Authentication Support Module
+//
+// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
+// (formerly draft-brezak-spnego-http-04.txt)
+//
+// Also described here:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
+//
+
+#include "nsAuthSSPI.h"
+#include "nsIServiceManager.h"
+#include "nsIDNSService.h"
+#include "nsIDNSRecord.h"
+#include "nsNetCID.h"
+#include "nsCOMPtr.h"
+#include "nsICryptoHash.h"
+#include "mozilla/Telemetry.h"
+
+#include <windows.h>
+
+#define SEC_SUCCESS(Status) ((Status) >= 0)
+
+#ifndef KERB_WRAP_NO_ENCRYPT
+#define KERB_WRAP_NO_ENCRYPT 0x80000001
+#endif
+
+#ifndef SECBUFFER_PADDING
+#define SECBUFFER_PADDING 9
+#endif
+
+#ifndef SECBUFFER_STREAM
+#define SECBUFFER_STREAM 10
+#endif
+
+//-----------------------------------------------------------------------------
+
+static const wchar_t *const pTypeName [] = {
+ L"Kerberos",
+ L"Negotiate",
+ L"NTLM"
+};
+
+#ifdef DEBUG
+#define CASE_(_x) case _x: return # _x;
+static const char *MapErrorCode(int rc)
+{
+ switch (rc) {
+ CASE_(SEC_E_OK)
+ CASE_(SEC_I_CONTINUE_NEEDED)
+ CASE_(SEC_I_COMPLETE_NEEDED)
+ CASE_(SEC_I_COMPLETE_AND_CONTINUE)
+ CASE_(SEC_E_INCOMPLETE_MESSAGE)
+ CASE_(SEC_I_INCOMPLETE_CREDENTIALS)
+ CASE_(SEC_E_INVALID_HANDLE)
+ CASE_(SEC_E_TARGET_UNKNOWN)
+ CASE_(SEC_E_LOGON_DENIED)
+ CASE_(SEC_E_INTERNAL_ERROR)
+ CASE_(SEC_E_NO_CREDENTIALS)
+ CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY)
+ CASE_(SEC_E_INSUFFICIENT_MEMORY)
+ CASE_(SEC_E_INVALID_TOKEN)
+ }
+ return "<unknown>";
+}
+#else
+#define MapErrorCode(_rc) ""
+#endif
+
+//-----------------------------------------------------------------------------
+
+static PSecurityFunctionTableW sspi;
+
+static nsresult
+InitSSPI()
+{
+ LOG((" InitSSPI\n"));
+
+ sspi = InitSecurityInterfaceW();
+ if (!sspi) {
+ LOG(("InitSecurityInterfaceW failed"));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+static nsresult
+MakeSN(const char *principal, nsCString &result)
+{
+ nsresult rv;
+
+ nsAutoCString buf(principal);
+
+ // The service name looks like "protocol@hostname", we need to map
+ // this to a value that SSPI expects. To be consistent with IE, we
+ // need to map '@' to '/' and canonicalize the hostname.
+ int32_t index = buf.FindChar('@');
+ if (index == kNotFound)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // This could be expensive if our DNS cache cannot satisfy the request.
+ // However, we should have at least hit the OS resolver once prior to
+ // reaching this code, so provided the OS resolver has this information
+ // cached, we should not have to worry about blocking on this function call
+ // for very long. NOTE: because we ask for the canonical hostname, we
+ // might end up requiring extra network activity in cases where the OS
+ // resolver might not have enough information to satisfy the request from
+ // its cache. This is not an issue in versions of Windows up to WinXP.
+ nsCOMPtr<nsIDNSRecord> record;
+ rv = dns->Resolve(Substring(buf, index + 1),
+ nsIDNSService::RESOLVE_CANONICAL_NAME,
+ getter_AddRefs(record));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString cname;
+ rv = record->GetCanonicalName(cname);
+ if (NS_SUCCEEDED(rv)) {
+ result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname;
+ LOG(("Using SPN of [%s]\n", result.get()));
+ }
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+nsAuthSSPI::nsAuthSSPI(pType package)
+ : mServiceFlags(REQ_DEFAULT)
+ , mMaxTokenLen(0)
+ , mPackage(package)
+ , mCertDERData(nullptr)
+ , mCertDERLength(0)
+{
+ memset(&mCred, 0, sizeof(mCred));
+ memset(&mCtxt, 0, sizeof(mCtxt));
+}
+
+nsAuthSSPI::~nsAuthSSPI()
+{
+ Reset();
+
+ if (mCred.dwLower || mCred.dwUpper) {
+#ifdef __MINGW32__
+ (sspi->FreeCredentialsHandle)(&mCred);
+#else
+ (sspi->FreeCredentialHandle)(&mCred);
+#endif
+ memset(&mCred, 0, sizeof(mCred));
+ }
+}
+
+void
+nsAuthSSPI::Reset()
+{
+ mIsFirst = true;
+
+ if (mCertDERData){
+ free(mCertDERData);
+ mCertDERData = nullptr;
+ mCertDERLength = 0;
+ }
+
+ if (mCtxt.dwLower || mCtxt.dwUpper) {
+ (sspi->DeleteSecurityContext)(&mCtxt);
+ memset(&mCtxt, 0, sizeof(mCtxt));
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule)
+
+NS_IMETHODIMP
+nsAuthSSPI::Init(const char *serviceName,
+ uint32_t serviceFlags,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password)
+{
+ LOG((" nsAuthSSPI::Init\n"));
+
+ mIsFirst = true;
+ mCertDERLength = 0;
+ mCertDERData = nullptr;
+
+ // The caller must supply a service name to be used. (For why we now require
+ // a service name for NTLM, see bug 487872.)
+ NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
+
+ nsresult rv;
+
+ // XXX lazy initialization like this assumes that we are single threaded
+ if (!sspi) {
+ rv = InitSSPI();
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ SEC_WCHAR *package;
+
+ package = (SEC_WCHAR *) pTypeName[(int)mPackage];
+
+ if (mPackage == PACKAGE_TYPE_NTLM) {
+ // (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups.
+ // The incoming serviceName is in the format: "protocol@hostname", SSPI expects
+ // "<service class>/<hostname>", so swap the '@' for a '/'.
+ mServiceName.Assign(serviceName);
+ int32_t index = mServiceName.FindChar('@');
+ if (index == kNotFound)
+ return NS_ERROR_UNEXPECTED;
+ mServiceName.Replace(index, 1, '/');
+ }
+ else {
+ // Kerberos requires the canonical host, MakeSN takes care of this through a
+ // DNS lookup.
+ rv = MakeSN(serviceName, mServiceName);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ mServiceFlags = serviceFlags;
+
+ SECURITY_STATUS rc;
+
+ PSecPkgInfoW pinfo;
+ rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo);
+ if (rc != SEC_E_OK) {
+ LOG(("%s package not found\n", package));
+ return NS_ERROR_UNEXPECTED;
+ }
+ mMaxTokenLen = pinfo->cbMaxToken;
+ (sspi->FreeContextBuffer)(pinfo);
+
+ MS_TimeStamp useBefore;
+
+ SEC_WINNT_AUTH_IDENTITY_W ai;
+ SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr;
+
+ // domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived
+ // returns false for identityInvalid. Use default credentials in this case by passing
+ // null for pai.
+ if (username && password) {
+ // Keep a copy of these strings for the duration
+ mUsername.Assign(username);
+ mPassword.Assign(password);
+ mDomain.Assign(domain);
+ ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting());
+ ai.DomainLength = mDomain.Length();
+ ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting());
+ ai.UserLength = mUsername.Length();
+ ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting());
+ ai.PasswordLength = mPassword.Length();
+ ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+ pai = &ai;
+ }
+
+ rc = (sspi->AcquireCredentialsHandleW)(nullptr,
+ package,
+ SECPKG_CRED_OUTBOUND,
+ nullptr,
+ pai,
+ nullptr,
+ nullptr,
+ &mCred,
+ &useBefore);
+ if (rc != SEC_E_OK)
+ return NS_ERROR_UNEXPECTED;
+
+ static bool sTelemetrySent = false;
+ if (!sTelemetrySent) {
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::NTLM_MODULE_USED_2,
+ serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
+ ? NTLM_MODULE_WIN_API_PROXY
+ : NTLM_MODULE_WIN_API_DIRECT);
+ sTelemetrySent = true;
+ }
+
+ LOG(("AcquireCredentialsHandle() succeeded.\n"));
+ return NS_OK;
+}
+
+// The arguments inToken and inTokenLen are used to pass in the server
+// certificate (when available) in the first call of the function. The
+// second time these arguments hold an input token.
+NS_IMETHODIMP
+nsAuthSSPI::GetNextToken(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ // String for end-point bindings.
+ const char end_point[] = "tls-server-end-point:";
+ const int end_point_length = sizeof(end_point) - 1;
+ const int hash_size = 32; // Size of a SHA256 hash.
+ const int cbt_size = hash_size + end_point_length;
+
+ SECURITY_STATUS rc;
+ MS_TimeStamp ignored;
+
+ DWORD ctxAttr, ctxReq = 0;
+ CtxtHandle *ctxIn;
+ SecBufferDesc ibd, obd;
+ // Optional second input buffer for the CBT (Channel Binding Token)
+ SecBuffer ib[2], ob;
+ // Pointer to the block of memory that stores the CBT
+ char* sspi_cbt = nullptr;
+ SEC_CHANNEL_BINDINGS pendpoint_binding;
+
+ LOG(("entering nsAuthSSPI::GetNextToken()\n"));
+
+ if (!mCred.dwLower && !mCred.dwUpper) {
+ LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (mServiceFlags & REQ_DELEGATE)
+ ctxReq |= ISC_REQ_DELEGATE;
+ if (mServiceFlags & REQ_MUTUAL_AUTH)
+ ctxReq |= ISC_REQ_MUTUAL_AUTH;
+
+ if (inToken) {
+ if (mIsFirst) {
+ // First time if it comes with a token,
+ // the token represents the server certificate.
+ mIsFirst = false;
+ mCertDERLength = inTokenLen;
+ mCertDERData = moz_xmalloc(inTokenLen);
+ if (!mCertDERData)
+ return NS_ERROR_OUT_OF_MEMORY;
+ memcpy(mCertDERData, inToken, inTokenLen);
+
+ // We are starting a new authentication sequence.
+ // If we have already initialized our
+ // security context, then we're in trouble because it means that the
+ // first sequence failed. We need to bail or else we might end up in
+ // an infinite loop.
+ if (mCtxt.dwLower || mCtxt.dwUpper) {
+ LOG(("Cannot restart authentication sequence!"));
+ return NS_ERROR_UNEXPECTED;
+ }
+ ctxIn = nullptr;
+ // The certificate needs to be erased before being passed
+ // to InitializeSecurityContextW().
+ inToken = nullptr;
+ inTokenLen = 0;
+ } else {
+ ibd.ulVersion = SECBUFFER_VERSION;
+ ibd.cBuffers = 0;
+ ibd.pBuffers = ib;
+
+ // If we have stored a certificate, the Channel Binding Token
+ // needs to be generated and sent in the first input buffer.
+ if (mCertDERLength > 0) {
+ // First we create a proper Endpoint Binding structure.
+ pendpoint_binding.dwInitiatorAddrType = 0;
+ pendpoint_binding.cbInitiatorLength = 0;
+ pendpoint_binding.dwInitiatorOffset = 0;
+ pendpoint_binding.dwAcceptorAddrType = 0;
+ pendpoint_binding.cbAcceptorLength = 0;
+ pendpoint_binding.dwAcceptorOffset = 0;
+ pendpoint_binding.cbApplicationDataLength = cbt_size;
+ pendpoint_binding.dwApplicationDataOffset =
+ sizeof(SEC_CHANNEL_BINDINGS);
+
+ // Then add it to the array of sec buffers accordingly.
+ ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+ ib[ibd.cBuffers].cbBuffer =
+ pendpoint_binding.cbApplicationDataLength
+ + pendpoint_binding.dwApplicationDataOffset;
+
+ sspi_cbt = (char *) moz_xmalloc(ib[ibd.cBuffers].cbBuffer);
+ if (!sspi_cbt){
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Helper to write in the memory block that stores the CBT
+ char* sspi_cbt_ptr = sspi_cbt;
+
+ ib[ibd.cBuffers].pvBuffer = sspi_cbt;
+ ibd.cBuffers++;
+
+ memcpy(sspi_cbt_ptr, &pendpoint_binding,
+ pendpoint_binding.dwApplicationDataOffset);
+ sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
+
+ memcpy(sspi_cbt_ptr, end_point, end_point_length);
+ sspi_cbt_ptr += end_point_length;
+
+ // Start hashing. We are always doing SHA256, but depending
+ // on the certificate, a different alogirthm might be needed.
+ nsAutoCString hashString;
+
+ nsresult rv;
+ nsCOMPtr<nsICryptoHash> crypto;
+ crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = crypto->Init(nsICryptoHash::SHA256);
+ if (NS_SUCCEEDED(rv))
+ rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
+ if (NS_SUCCEEDED(rv))
+ rv = crypto->Finish(false, hashString);
+ if (NS_FAILED(rv)) {
+ free(mCertDERData);
+ mCertDERData = nullptr;
+ mCertDERLength = 0;
+ free(sspi_cbt);
+ return rv;
+ }
+
+ // Once the hash has been computed, we store it in memory right
+ // after the Endpoint structure and the "tls-server-end-point:"
+ // char array.
+ memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
+
+ // Free memory used to store the server certificate
+ free(mCertDERData);
+ mCertDERData = nullptr;
+ mCertDERLength = 0;
+ } // End of CBT computation.
+
+ // We always need this SECBUFFER.
+ ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
+ ib[ibd.cBuffers].cbBuffer = inTokenLen;
+ ib[ibd.cBuffers].pvBuffer = (void *) inToken;
+ ibd.cBuffers++;
+ ctxIn = &mCtxt;
+ }
+ } else { // First time and without a token (no server certificate)
+ // We are starting a new authentication sequence. If we have already
+ // initialized our security context, then we're in trouble because it
+ // means that the first sequence failed. We need to bail or else we
+ // might end up in an infinite loop.
+ if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
+ LOG(("Cannot restart authentication sequence!"));
+ return NS_ERROR_UNEXPECTED;
+ }
+ ctxIn = nullptr;
+ mIsFirst = false;
+ }
+
+ obd.ulVersion = SECBUFFER_VERSION;
+ obd.cBuffers = 1;
+ obd.pBuffers = &ob;
+ ob.BufferType = SECBUFFER_TOKEN;
+ ob.cbBuffer = mMaxTokenLen;
+ ob.pvBuffer = moz_xmalloc(ob.cbBuffer);
+ if (!ob.pvBuffer){
+ if (sspi_cbt)
+ free(sspi_cbt);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memset(ob.pvBuffer, 0, ob.cbBuffer);
+
+ NS_ConvertUTF8toUTF16 wSN(mServiceName);
+ SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
+
+ rc = (sspi->InitializeSecurityContextW)(&mCred,
+ ctxIn,
+ sn,
+ ctxReq,
+ 0,
+ SECURITY_NATIVE_DREP,
+ inToken ? &ibd : nullptr,
+ 0,
+ &mCtxt,
+ &obd,
+ &ctxAttr,
+ &ignored);
+ if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
+
+ if (rc == SEC_E_OK)
+ LOG(("InitializeSecurityContext: succeeded.\n"));
+ else
+ LOG(("InitializeSecurityContext: continue.\n"));
+
+ if (sspi_cbt)
+ free(sspi_cbt);
+
+ if (!ob.cbBuffer) {
+ free(ob.pvBuffer);
+ ob.pvBuffer = nullptr;
+ }
+ *outToken = ob.pvBuffer;
+ *outTokenLen = ob.cbBuffer;
+
+ if (rc == SEC_E_OK)
+ return NS_SUCCESS_AUTH_FINISHED;
+
+ return NS_OK;
+ }
+
+ LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc)));
+ Reset();
+ free(ob.pvBuffer);
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAuthSSPI::Unwrap(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ SECURITY_STATUS rc;
+ SecBufferDesc ibd;
+ SecBuffer ib[2];
+
+ ibd.cBuffers = 2;
+ ibd.pBuffers = ib;
+ ibd.ulVersion = SECBUFFER_VERSION;
+
+ // SSPI Buf
+ ib[0].BufferType = SECBUFFER_STREAM;
+ ib[0].cbBuffer = inTokenLen;
+ ib[0].pvBuffer = moz_xmalloc(ib[0].cbBuffer);
+ if (!ib[0].pvBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ memcpy(ib[0].pvBuffer, inToken, inTokenLen);
+
+ // app data
+ ib[1].BufferType = SECBUFFER_DATA;
+ ib[1].cbBuffer = 0;
+ ib[1].pvBuffer = nullptr;
+
+ rc = (sspi->DecryptMessage)(
+ &mCtxt,
+ &ibd,
+ 0, // no sequence numbers
+ nullptr
+ );
+
+ if (SEC_SUCCESS(rc)) {
+ // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which
+ // case we can let the caller free it. Otherwise, we need to
+ // clone it, and free the original
+ if (ib[0].pvBuffer == ib[1].pvBuffer) {
+ *outToken = ib[1].pvBuffer;
+ }
+ else {
+ *outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer);
+ free(ib[0].pvBuffer);
+ if (!*outToken)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *outTokenLen = ib[1].cbBuffer;
+ }
+ else
+ free(ib[0].pvBuffer);
+
+ if (!SEC_SUCCESS(rc))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+// utility class used to free memory on exit
+class secBuffers
+{
+public:
+
+ SecBuffer ib[3];
+
+ secBuffers() { memset(&ib, 0, sizeof(ib)); }
+
+ ~secBuffers()
+ {
+ if (ib[0].pvBuffer)
+ free(ib[0].pvBuffer);
+
+ if (ib[1].pvBuffer)
+ free(ib[1].pvBuffer);
+
+ if (ib[2].pvBuffer)
+ free(ib[2].pvBuffer);
+ }
+};
+
+NS_IMETHODIMP
+nsAuthSSPI::Wrap(const void *inToken,
+ uint32_t inTokenLen,
+ bool confidential,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ SECURITY_STATUS rc;
+
+ SecBufferDesc ibd;
+ secBuffers bufs;
+ SecPkgContext_Sizes sizes;
+
+ rc = (sspi->QueryContextAttributesW)(
+ &mCtxt,
+ SECPKG_ATTR_SIZES,
+ &sizes);
+
+ if (!SEC_SUCCESS(rc))
+ return NS_ERROR_FAILURE;
+
+ ibd.cBuffers = 3;
+ ibd.pBuffers = bufs.ib;
+ ibd.ulVersion = SECBUFFER_VERSION;
+
+ // SSPI
+ bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer;
+ bufs.ib[0].BufferType = SECBUFFER_TOKEN;
+ bufs.ib[0].pvBuffer = moz_xmalloc(sizes.cbSecurityTrailer);
+
+ if (!bufs.ib[0].pvBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // APP Data
+ bufs.ib[1].BufferType = SECBUFFER_DATA;
+ bufs.ib[1].pvBuffer = moz_xmalloc(inTokenLen);
+ bufs.ib[1].cbBuffer = inTokenLen;
+
+ if (!bufs.ib[1].pvBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen);
+
+ // SSPI
+ bufs.ib[2].BufferType = SECBUFFER_PADDING;
+ bufs.ib[2].cbBuffer = sizes.cbBlockSize;
+ bufs.ib[2].pvBuffer = moz_xmalloc(bufs.ib[2].cbBuffer);
+
+ if (!bufs.ib[2].pvBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rc = (sspi->EncryptMessage)(&mCtxt,
+ confidential ? 0 : KERB_WRAP_NO_ENCRYPT,
+ &ibd, 0);
+
+ if (SEC_SUCCESS(rc)) {
+ int len = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer;
+ char *p = (char *) moz_xmalloc(len);
+
+ if (!p)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *outToken = (void *) p;
+ *outTokenLen = len;
+
+ memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer);
+ p += bufs.ib[0].cbBuffer;
+
+ memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer);
+ p += bufs.ib[1].cbBuffer;
+
+ memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer);
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/extensions/auth/nsAuthSSPI.h b/extensions/auth/nsAuthSSPI.h
new file mode 100644
index 000000000..752b22aef
--- /dev/null
+++ b/extensions/auth/nsAuthSSPI.h
@@ -0,0 +1,59 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAuthSSPI_h__
+#define nsAuthSSPI_h__
+
+#include "nsAuth.h"
+#include "nsIAuthModule.h"
+#include "nsString.h"
+
+#include <windows.h>
+
+#define SECURITY_WIN32 1
+#include <ntsecapi.h>
+#include <security.h>
+#include <rpc.h>
+
+// The nsNegotiateAuth class provides responses for the GSS-API Negotiate method
+// as specified by Microsoft in draft-brezak-spnego-http-04.txt
+
+// It can also be configured to talk raw NTLM. This implementation of NTLM has
+// the advantage of being able to access the user's logon credentials. This
+// implementation of NTLM should only be used for single-signon. It should be
+// avoided when authenticating over the internet since it may use a lower-grade
+// version of password hashing depending on the version of Windows being used.
+
+class nsAuthSSPI final : public nsIAuthModule
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIAUTHMODULE
+
+ nsAuthSSPI(pType package = PACKAGE_TYPE_NEGOTIATE);
+
+private:
+ ~nsAuthSSPI();
+
+ void Reset();
+
+ typedef TimeStamp MS_TimeStamp;
+
+private:
+ CredHandle mCred;
+ CtxtHandle mCtxt;
+ nsCString mServiceName;
+ uint32_t mServiceFlags;
+ uint32_t mMaxTokenLen;
+ pType mPackage;
+ nsString mDomain;
+ nsString mUsername;
+ nsString mPassword;
+ bool mIsFirst;
+ void* mCertDERData;
+ uint32_t mCertDERLength;
+};
+
+#endif /* nsAuthSSPI_h__ */
diff --git a/extensions/auth/nsAuthSambaNTLM.cpp b/extensions/auth/nsAuthSambaNTLM.cpp
new file mode 100644
index 000000000..1406c15e0
--- /dev/null
+++ b/extensions/auth/nsAuthSambaNTLM.cpp
@@ -0,0 +1,296 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAuth.h"
+#include "nsAuthSambaNTLM.h"
+#include "prenv.h"
+#include "plbase64.h"
+#include "prerror.h"
+#include "mozilla/Telemetry.h"
+
+#include <stdlib.h>
+
+nsAuthSambaNTLM::nsAuthSambaNTLM()
+ : mInitialMessage(nullptr), mChildPID(nullptr), mFromChildFD(nullptr),
+ mToChildFD(nullptr)
+{
+}
+
+nsAuthSambaNTLM::~nsAuthSambaNTLM()
+{
+ // ntlm_auth reads from stdin regularly so closing our file handles
+ // should cause it to exit.
+ Shutdown();
+ free(mInitialMessage);
+}
+
+void
+nsAuthSambaNTLM::Shutdown()
+{
+ if (mFromChildFD) {
+ PR_Close(mFromChildFD);
+ mFromChildFD = nullptr;
+ }
+ if (mToChildFD) {
+ PR_Close(mToChildFD);
+ mToChildFD = nullptr;
+ }
+ if (mChildPID) {
+ int32_t exitCode;
+ PR_WaitProcess(mChildPID, &exitCode);
+ mChildPID = nullptr;
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsAuthSambaNTLM, nsIAuthModule)
+
+static bool
+SpawnIOChild(char* const* aArgs, PRProcess** aPID,
+ PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
+{
+ PRFileDesc* toChildPipeRead;
+ PRFileDesc* toChildPipeWrite;
+ if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
+ return false;
+ PR_SetFDInheritable(toChildPipeRead, true);
+ PR_SetFDInheritable(toChildPipeWrite, false);
+
+ PRFileDesc* fromChildPipeRead;
+ PRFileDesc* fromChildPipeWrite;
+ if (PR_CreatePipe(&fromChildPipeRead, &fromChildPipeWrite) != PR_SUCCESS) {
+ PR_Close(toChildPipeRead);
+ PR_Close(toChildPipeWrite);
+ return false;
+ }
+ PR_SetFDInheritable(fromChildPipeRead, false);
+ PR_SetFDInheritable(fromChildPipeWrite, true);
+
+ PRProcessAttr* attr = PR_NewProcessAttr();
+ if (!attr) {
+ PR_Close(fromChildPipeRead);
+ PR_Close(fromChildPipeWrite);
+ PR_Close(toChildPipeRead);
+ PR_Close(toChildPipeWrite);
+ return false;
+ }
+
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, toChildPipeRead);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, fromChildPipeWrite);
+
+ PRProcess* process = PR_CreateProcess(aArgs[0], aArgs, nullptr, attr);
+ PR_DestroyProcessAttr(attr);
+ PR_Close(fromChildPipeWrite);
+ PR_Close(toChildPipeRead);
+ if (!process) {
+ LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
+ PR_Close(fromChildPipeRead);
+ PR_Close(toChildPipeWrite);
+ return false;
+ }
+
+ *aPID = process;
+ *aFromChildFD = fromChildPipeRead;
+ *aToChildFD = toChildPipeWrite;
+ return true;
+}
+
+static bool WriteString(PRFileDesc* aFD, const nsACString& aString)
+{
+ int32_t length = aString.Length();
+ const char* s = aString.BeginReading();
+ LOG(("Writing to ntlm_auth: %s", s));
+
+ while (length > 0) {
+ int result = PR_Write(aFD, s, length);
+ if (result <= 0)
+ return false;
+ s += result;
+ length -= result;
+ }
+ return true;
+}
+
+static bool ReadLine(PRFileDesc* aFD, nsACString& aString)
+{
+ // ntlm_auth is defined to only send one line in response to each of our
+ // input lines. So this simple unbuffered strategy works as long as we
+ // read the response immediately after sending one request.
+ aString.Truncate();
+ for (;;) {
+ char buf[1024];
+ int result = PR_Read(aFD, buf, sizeof(buf));
+ if (result <= 0)
+ return false;
+ aString.Append(buf, result);
+ if (buf[result - 1] == '\n') {
+ LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get()));
+ return true;
+ }
+ }
+}
+
+/**
+ * Returns a heap-allocated array of PRUint8s, and stores the length in aLen.
+ * Returns nullptr if there's an error of any kind.
+ */
+static uint8_t* ExtractMessage(const nsACString& aLine, uint32_t* aLen)
+{
+ // ntlm_auth sends blobs to us as base64-encoded strings after the "xx "
+ // preamble on the response line.
+ int32_t length = aLine.Length();
+ // The caller should verify there is a valid "xx " prefix and the line
+ // is terminated with a \n
+ NS_ASSERTION(length >= 4, "Line too short...");
+ const char* line = aLine.BeginReading();
+ const char* s = line + 3;
+ length -= 4; // lose first 3 chars plus trailing \n
+ NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated");
+
+ if (length & 3) {
+ // The base64 encoded block must be multiple of 4. If not, something
+ // screwed up.
+ NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
+ return nullptr;
+ }
+
+ // Calculate the exact length. I wonder why there isn't a function for this
+ // in plbase64.
+ int32_t numEquals;
+ for (numEquals = 0; numEquals < length; ++numEquals) {
+ if (s[length - 1 - numEquals] != '=')
+ break;
+ }
+ *aLen = (length/4)*3 - numEquals;
+ return reinterpret_cast<uint8_t*>(PL_Base64Decode(s, length, nullptr));
+}
+
+nsresult
+nsAuthSambaNTLM::SpawnNTLMAuthHelper()
+{
+ const char* username = PR_GetEnv("USER");
+ if (!username)
+ return NS_ERROR_FAILURE;
+
+ const char* const args[] = {
+ "ntlm_auth",
+ "--helper-protocol", "ntlmssp-client-1",
+ "--use-cached-creds",
+ "--username", username,
+ nullptr
+ };
+
+ bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID, &mFromChildFD, &mToChildFD);
+ if (!isOK)
+ return NS_ERROR_FAILURE;
+
+ if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
+ return NS_ERROR_FAILURE;
+ nsCString line;
+ if (!ReadLine(mFromChildFD, line))
+ return NS_ERROR_FAILURE;
+ if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) {
+ // Something went wrong. Perhaps no credentials are accessible.
+ return NS_ERROR_FAILURE;
+ }
+
+ // It gave us an initial client-to-server request packet. Save that
+ // because we'll need it later.
+ mInitialMessage = ExtractMessage(line, &mInitialMessageLen);
+ if (!mInitialMessage)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAuthSambaNTLM::Init(const char *serviceName,
+ uint32_t serviceFlags,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password)
+{
+ NS_ASSERTION(!username && !domain && !password, "unexpected credentials");
+
+ static bool sTelemetrySent = false;
+ if (!sTelemetrySent) {
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::NTLM_MODULE_USED_2,
+ serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
+ ? NTLM_MODULE_SAMBA_AUTH_PROXY
+ : NTLM_MODULE_SAMBA_AUTH_DIRECT);
+ sTelemetrySent = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAuthSambaNTLM::GetNextToken(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ if (!inToken) {
+ /* someone wants our initial message */
+ *outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen);
+ if (!*outToken)
+ return NS_ERROR_OUT_OF_MEMORY;
+ *outTokenLen = mInitialMessageLen;
+ return NS_OK;
+ }
+
+ /* inToken must be a type 2 message. Get ntlm_auth to generate our response */
+ char* encoded = PL_Base64Encode(static_cast<const char*>(inToken), inTokenLen, nullptr);
+ if (!encoded)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCString request;
+ request.AssignLiteral("TT ");
+ request.Append(encoded);
+ free(encoded);
+ request.Append('\n');
+
+ if (!WriteString(mToChildFD, request))
+ return NS_ERROR_FAILURE;
+ nsCString line;
+ if (!ReadLine(mFromChildFD, line))
+ return NS_ERROR_FAILURE;
+ if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK ")) &&
+ !StringBeginsWith(line, NS_LITERAL_CSTRING("AF "))) {
+ // Something went wrong. Perhaps no credentials are accessible.
+ return NS_ERROR_FAILURE;
+ }
+ uint8_t* buf = ExtractMessage(line, outTokenLen);
+ if (!buf)
+ return NS_ERROR_FAILURE;
+ *outToken = nsMemory::Clone(buf, *outTokenLen);
+ free(buf);
+ if (!*outToken) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // We're done. Close our file descriptors now and reap the helper
+ // process.
+ Shutdown();
+ return NS_SUCCESS_AUTH_FINISHED;
+}
+
+NS_IMETHODIMP
+nsAuthSambaNTLM::Unwrap(const void *inToken,
+ uint32_t inTokenLen,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsAuthSambaNTLM::Wrap(const void *inToken,
+ uint32_t inTokenLen,
+ bool confidential,
+ void **outToken,
+ uint32_t *outTokenLen)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/extensions/auth/nsAuthSambaNTLM.h b/extensions/auth/nsAuthSambaNTLM.h
new file mode 100644
index 000000000..26b21b667
--- /dev/null
+++ b/extensions/auth/nsAuthSambaNTLM.h
@@ -0,0 +1,52 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAuthSambaNTLM_h__
+#define nsAuthSambaNTLM_h__
+
+#include "nsIAuthModule.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "prio.h"
+#include "prproces.h"
+#include "mozilla/Attributes.h"
+
+/**
+ * This is an implementation of NTLM authentication that does single-signon
+ * by obtaining the user's Unix username, parsing it into DOMAIN\name format,
+ * and then asking Samba's ntlm_auth tool to do the authentication for us
+ * using the user's password cached in winbindd, if available. If the
+ * password is not available then this component fails to instantiate so
+ * nsHttpNTLMAuth will fall back to a different NTLM implementation.
+ * NOTE: at time of writing, this requires patches to be added to the stock
+ * Samba winbindd and ntlm_auth!
+ */
+class nsAuthSambaNTLM final : public nsIAuthModule
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIAUTHMODULE
+
+ nsAuthSambaNTLM();
+
+ // We spawn the ntlm_auth helper from the module constructor, because
+ // that lets us fail to instantiate the module if ntlm_auth isn't
+ // available, triggering fallback to the built-in NTLM support (which
+ // doesn't support single signon, of course)
+ nsresult SpawnNTLMAuthHelper();
+
+private:
+ ~nsAuthSambaNTLM();
+
+ void Shutdown();
+
+ uint8_t* mInitialMessage; /* free with free() */
+ uint32_t mInitialMessageLen;
+ PRProcess* mChildPID;
+ PRFileDesc* mFromChildFD;
+ PRFileDesc* mToChildFD;
+};
+
+#endif /* nsAuthSambaNTLM_h__ */
diff --git a/extensions/auth/nsHttpNegotiateAuth.cpp b/extensions/auth/nsHttpNegotiateAuth.cpp
new file mode 100644
index 000000000..adea54b85
--- /dev/null
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -0,0 +1,731 @@
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// HTTP Negotiate Authentication Support Module
+//
+// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
+// (formerly draft-brezak-spnego-http-04.txt)
+//
+// Also described here:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
+//
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "nsAuth.h"
+#include "nsHttpNegotiateAuth.h"
+
+#include "nsIHttpAuthenticableChannel.h"
+#include "nsIProxiedChannel.h"
+#include "nsIAuthModule.h"
+#include "nsIServiceManager.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIProxyInfo.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsNetCID.h"
+#include "plbase64.h"
+#include "plstr.h"
+#include "mozilla/Base64.h"
+#include "prprf.h"
+#include "mozilla/Logging.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Sprintf.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "nsIHttpAuthenticatorCallback.h"
+#include "mozilla/Mutex.h"
+#include "nsICancelable.h"
+
+using mozilla::Base64Decode;
+
+//-----------------------------------------------------------------------------
+
+static const char kNegotiate[] = "Negotiate";
+static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
+static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
+static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
+static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
+static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
+static const char kSSOinPBmode[] = "network.auth.private-browsing-sso";
+
+#define kNegotiateLen (sizeof(kNegotiate)-1)
+#define DEFAULT_THREAD_TIMEOUT_MS 30000
+
+//-----------------------------------------------------------------------------
+
+// Return false when the channel comes from a Private browsing window.
+static bool
+TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel, bool proxyAuth)
+{
+ // Proxy should go all the time, it's not considered a privacy leak
+ // to send default credentials to a proxy.
+ if (proxyAuth) {
+ return true;
+ }
+
+ nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel);
+ MOZ_ASSERT(bareChannel);
+
+ if (!NS_UsePrivateBrowsing(bareChannel)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ bool ssoInPb;
+ if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) && ssoInPb) {
+ return true;
+ }
+
+ // When the "Never remember history" option is set, all channels are
+ // set PB mode flag, but here we want to make an exception, users
+ // want their credentials go out.
+ bool dontRememberHistory;
+ if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart",
+ &dontRememberHistory)) &&
+ dontRememberHistory) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags)
+{
+ //
+ // Negotiate Auth creds should not be reused across multiple requests.
+ // Only perform the negotiation when it is explicitly requested by the
+ // server. Thus, do *NOT* use the "REUSABLE_CREDENTIALS" flag here.
+ //
+ // CONNECTION_BASED is specified instead of REQUEST_BASED since we need
+ // to complete a sequence of transactions with the server over the same
+ // connection.
+ //
+ *flags = CONNECTION_BASED | IDENTITY_IGNORED;
+ return NS_OK;
+}
+
+//
+// Always set *identityInvalid == FALSE here. This
+// will prevent the browser from popping up the authentication
+// prompt window. Because GSSAPI does not have an API
+// for fetching initial credentials (ex: A Kerberos TGT),
+// there is no correct way to get the users credentials.
+//
+NS_IMETHODIMP
+nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel,
+ const char *challenge,
+ bool isProxyAuth,
+ nsISupports **sessionState,
+ nsISupports **continuationState,
+ bool *identityInvalid)
+{
+ nsIAuthModule *module = (nsIAuthModule *) *continuationState;
+
+ *identityInvalid = false;
+ if (module)
+ return NS_OK;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIURI> uri;
+ rv = authChannel->GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv))
+ return rv;
+
+ uint32_t req_flags = nsIAuthModule::REQ_DEFAULT;
+ nsAutoCString service;
+
+ if (isProxyAuth) {
+ if (!TestBoolPref(kNegotiateAuthAllowProxies)) {
+ LOG(("nsHttpNegotiateAuth::ChallengeReceived proxy auth blocked\n"));
+ return NS_ERROR_ABORT;
+ }
+
+ req_flags |= nsIAuthModule::REQ_PROXY_AUTH;
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ authChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
+ NS_ENSURE_STATE(proxyInfo);
+
+ proxyInfo->GetHost(service);
+ }
+ else {
+ bool allowed = TestNotInPBMode(authChannel, isProxyAuth) &&
+ (TestNonFqdn(uri) ||
+ TestPref(uri, kNegotiateAuthTrustedURIs));
+ if (!allowed) {
+ LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
+ return NS_ERROR_ABORT;
+ }
+
+ bool delegation = TestPref(uri, kNegotiateAuthDelegationURIs);
+ if (delegation) {
+ LOG((" using REQ_DELEGATE\n"));
+ req_flags |= nsIAuthModule::REQ_DELEGATE;
+ }
+
+ rv = uri->GetAsciiHost(service);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ LOG((" service = %s\n", service.get()));
+
+ //
+ // The correct service name for IIS servers is "HTTP/f.q.d.n", so
+ // construct the proper service name for passing to "gss_import_name".
+ //
+ // TODO: Possibly make this a configurable service name for use
+ // with non-standard servers that use stuff like "khttp/f.q.d.n"
+ // instead.
+ //
+ service.Insert("HTTP@", 0);
+
+ const char *contractID;
+ if (TestBoolPref(kNegotiateAuthSSPI)) {
+ LOG((" using negotiate-sspi\n"));
+ contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi";
+ }
+ else {
+ LOG((" using negotiate-gss\n"));
+ contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss";
+ }
+
+ rv = CallCreateInstance(contractID, &module);
+
+ if (NS_FAILED(rv)) {
+ LOG((" Failed to load Negotiate Module \n"));
+ return rv;
+ }
+
+ rv = module->Init(service.get(), req_flags, nullptr, nullptr, nullptr);
+
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(module);
+ return rv;
+ }
+
+ *continuationState = module;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsHttpNegotiateAuth, nsIHttpAuthenticator)
+
+namespace {
+
+//
+// GetNextTokenCompleteEvent
+//
+// This event is fired on main thread when async call of
+// nsHttpNegotiateAuth::GenerateCredentials is finished. During the Run()
+// method the nsIHttpAuthenticatorCallback::OnCredsAvailable is called with
+// obtained credentials, flags and NS_OK when successful, otherwise
+// NS_ERROR_FAILURE is returned as a result of failed operation.
+//
+class GetNextTokenCompleteEvent final : public nsIRunnable,
+ public nsICancelable
+{
+ virtual ~GetNextTokenCompleteEvent()
+ {
+ if (mCreds) {
+ free(mCreds);
+ }
+ };
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit GetNextTokenCompleteEvent(nsIHttpAuthenticatorCallback* aCallback)
+ : mCallback(aCallback)
+ , mCreds(nullptr)
+ , mCancelled(false)
+ {
+ }
+
+ NS_IMETHODIMP DispatchSuccess(char *aCreds,
+ uint32_t aFlags,
+ already_AddRefed<nsISupports> aSessionState,
+ already_AddRefed<nsISupports> aContinuationState)
+ {
+ // Called from worker thread
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mCreds = aCreds;
+ mFlags = aFlags;
+ mResult = NS_OK;
+ mSessionState = aSessionState;
+ mContinuationState = aContinuationState;
+ return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+ }
+
+ NS_IMETHODIMP DispatchError(already_AddRefed<nsISupports> aSessionState,
+ already_AddRefed<nsISupports> aContinuationState)
+ {
+ // Called from worker thread
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mResult = NS_ERROR_FAILURE;
+ mSessionState = aSessionState;
+ mContinuationState = aContinuationState;
+ return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+ }
+
+ NS_IMETHODIMP Run() override
+ {
+ // Runs on main thread
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mCancelled) {
+ nsCOMPtr<nsIHttpAuthenticatorCallback> callback;
+ callback.swap(mCallback);
+ callback->OnCredsGenerated(mCreds, mFlags, mResult, mSessionState, mContinuationState);
+ }
+ return NS_OK;
+ }
+
+ NS_IMETHODIMP Cancel(nsresult aReason) override
+ {
+ // Supposed to be called from main thread
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mCancelled = true;
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIHttpAuthenticatorCallback> mCallback;
+ char *mCreds; // This class owns it, freed in destructor
+ uint32_t mFlags;
+ nsresult mResult;
+ bool mCancelled;
+ nsCOMPtr<nsISupports> mSessionState;
+ nsCOMPtr<nsISupports> mContinuationState;
+};
+
+NS_IMPL_ISUPPORTS(GetNextTokenCompleteEvent, nsIRunnable, nsICancelable)
+
+//
+// GetNextTokenRunnable
+//
+// This runnable is created by GenerateCredentialsAsync and it runs
+// in nsHttpNegotiateAuth::mNegotiateThread and calling GenerateCredentials.
+//
+class GetNextTokenRunnable final : public mozilla::Runnable
+{
+ virtual ~GetNextTokenRunnable() {}
+ public:
+ GetNextTokenRunnable(nsIHttpAuthenticableChannel *authChannel,
+ const char *challenge,
+ bool isProxyAuth,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password,
+ nsISupports *sessionState,
+ nsISupports *continuationState,
+ GetNextTokenCompleteEvent *aCompleteEvent
+ )
+ : mAuthChannel(authChannel)
+ , mChallenge(challenge)
+ , mIsProxyAuth(isProxyAuth)
+ , mDomain(domain)
+ , mUsername(username)
+ , mPassword(password)
+ , mSessionState(sessionState)
+ , mContinuationState(continuationState)
+ , mCompleteEvent(aCompleteEvent)
+ {
+ }
+
+ NS_IMETHODIMP Run() override
+ {
+ // Runs on worker thread
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ char *creds;
+ uint32_t flags;
+ nsresult rv = ObtainCredentialsAndFlags(&creds, &flags);
+
+ // Passing session and continuation state this way to not touch
+ // referencing of the object that may not be thread safe.
+ // Not having a thread safe referencing doesn't mean the object
+ // cannot be used on multiple threads (one example is nsAuthSSPI.)
+ // This ensures state objects will be destroyed on the main thread
+ // when not changed by GenerateCredentials.
+ if (NS_FAILED(rv)) {
+ return mCompleteEvent->DispatchError(mSessionState.forget(),
+ mContinuationState.forget());
+ }
+
+ return mCompleteEvent->DispatchSuccess(creds, flags,
+ mSessionState.forget(),
+ mContinuationState.forget());
+ }
+
+ NS_IMETHODIMP ObtainCredentialsAndFlags(char **aCreds, uint32_t *aFlags)
+ {
+ nsresult rv;
+
+ // Use negotiate service to call GenerateCredentials outside of main thread
+ nsAutoCString contractId;
+ contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
+ contractId.Append("negotiate");
+ nsCOMPtr<nsIHttpAuthenticator> authenticator =
+ do_GetService(contractId.get(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsISupports *sessionState = mSessionState;
+ nsISupports *continuationState = mContinuationState;
+ // The continuationState is for the sake of completeness propagated
+ // to the caller (despite it is not changed in any GenerateCredentials
+ // implementation).
+ //
+ // The only implementation that use sessionState is the
+ // nsHttpDigestAuth::GenerateCredentials. Since there's no reason
+ // to implement nsHttpDigestAuth::GenerateCredentialsAsync
+ // because digest auth does not block the main thread, we won't
+ // propagate changes to sessionState to the caller because of
+ // the change is too complicated on the caller side.
+ //
+ // Should any of the session or continuation states change inside
+ // this method, they must be threadsafe.
+ rv = authenticator->GenerateCredentials(mAuthChannel,
+ mChallenge.get(),
+ mIsProxyAuth,
+ mDomain.get(),
+ mUsername.get(),
+ mPassword.get(),
+ &sessionState,
+ &continuationState,
+ aFlags,
+ aCreds);
+ if (mSessionState != sessionState) {
+ mSessionState = sessionState;
+ }
+ if (mContinuationState != continuationState) {
+ mContinuationState = continuationState;
+ }
+ return rv;
+ }
+ private:
+ nsCOMPtr<nsIHttpAuthenticableChannel> mAuthChannel;
+ nsCString mChallenge;
+ bool mIsProxyAuth;
+ nsString mDomain;
+ nsString mUsername;
+ nsString mPassword;
+ nsCOMPtr<nsISupports> mSessionState;
+ nsCOMPtr<nsISupports> mContinuationState;
+ RefPtr<GetNextTokenCompleteEvent> mCompleteEvent;
+};
+
+} // anonymous namespace
+
+NS_IMETHODIMP
+nsHttpNegotiateAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel,
+ nsIHttpAuthenticatorCallback* aCallback,
+ const char *challenge,
+ bool isProxyAuth,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password,
+ nsISupports *sessionState,
+ nsISupports *continuationState,
+ nsICancelable **aCancelable)
+{
+ NS_ENSURE_ARG(aCallback);
+ NS_ENSURE_ARG_POINTER(aCancelable);
+
+ RefPtr<GetNextTokenCompleteEvent> cancelEvent =
+ new GetNextTokenCompleteEvent(aCallback);
+
+
+ nsCOMPtr<nsIRunnable> getNextTokenRunnable =
+ new GetNextTokenRunnable(authChannel,
+ challenge,
+ isProxyAuth,
+ domain,
+ username,
+ password,
+ sessionState,
+ continuationState,
+ cancelEvent);
+ cancelEvent.forget(aCancelable);
+
+ nsresult rv;
+ if (!mNegotiateThread) {
+ mNegotiateThread =
+ new mozilla::LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+ NS_LITERAL_CSTRING("NegotiateAuth"));
+ NS_ENSURE_TRUE(mNegotiateThread, NS_ERROR_OUT_OF_MEMORY);
+ }
+ rv = mNegotiateThread->Dispatch(getNextTokenRunnable, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+//
+// GenerateCredentials
+//
+// This routine is responsible for creating the correct authentication
+// blob to pass to the server that requested "Negotiate" authentication.
+//
+NS_IMETHODIMP
+nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
+ const char *challenge,
+ bool isProxyAuth,
+ const char16_t *domain,
+ const char16_t *username,
+ const char16_t *password,
+ nsISupports **sessionState,
+ nsISupports **continuationState,
+ uint32_t *flags,
+ char **creds)
+{
+ // ChallengeReceived must have been called previously.
+ nsIAuthModule *module = (nsIAuthModule *) *continuationState;
+ NS_ENSURE_TRUE(module, NS_ERROR_NOT_INITIALIZED);
+
+ *flags = USING_INTERNAL_IDENTITY;
+
+ LOG(("nsHttpNegotiateAuth::GenerateCredentials() [challenge=%s]\n", challenge));
+
+ NS_ASSERTION(creds, "null param");
+
+#ifdef DEBUG
+ bool isGssapiAuth =
+ !PL_strncasecmp(challenge, kNegotiate, kNegotiateLen);
+ NS_ASSERTION(isGssapiAuth, "Unexpected challenge");
+#endif
+
+ //
+ // If the "Negotiate:" header had some data associated with it,
+ // that data should be used as the input to this call. This may
+ // be a continuation of an earlier call because GSSAPI authentication
+ // often takes multiple round-trips to complete depending on the
+ // context flags given. We want to use MUTUAL_AUTHENTICATION which
+ // generally *does* require multiple round-trips. Don't assume
+ // auth can be completed in just 1 call.
+ //
+ unsigned int len = strlen(challenge);
+
+ void *inToken, *outToken;
+ uint32_t inTokenLen, outTokenLen;
+
+ if (len > kNegotiateLen) {
+ challenge += kNegotiateLen;
+ while (*challenge == ' ')
+ challenge++;
+ len = strlen(challenge);
+
+ // strip off any padding (see bug 230351)
+ while (challenge[len - 1] == '=')
+ len--;
+
+ //
+ // Decode the response that followed the "Negotiate" token
+ //
+ nsresult rv =
+ Base64Decode(challenge, len, (char**)&inToken, &inTokenLen);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ else {
+ //
+ // Initializing, don't use an input token.
+ //
+ inToken = nullptr;
+ inTokenLen = 0;
+ }
+
+ nsresult rv = module->GetNextToken(inToken, inTokenLen, &outToken, &outTokenLen);
+
+ free(inToken);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (outTokenLen == 0) {
+ LOG((" No output token to send, exiting"));
+ return NS_ERROR_FAILURE;
+ }
+
+ //
+ // base64 encode the output token.
+ //
+ char *encoded_token = PL_Base64Encode((char *)outToken, outTokenLen, nullptr);
+
+ free(outToken);
+
+ if (!encoded_token)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ LOG((" Sending a token of length %d\n", outTokenLen));
+
+ // allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0")
+ const int bufsize = kNegotiateLen + 1 + strlen(encoded_token) + 1;
+ *creds = (char *) moz_xmalloc(bufsize);
+ if (MOZ_UNLIKELY(!*creds))
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ else
+ snprintf(*creds, bufsize, "%s %s", kNegotiate, encoded_token);
+
+ PR_Free(encoded_token);
+ return rv;
+}
+
+bool
+nsHttpNegotiateAuth::TestBoolPref(const char *pref)
+{
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (!prefs)
+ return false;
+
+ bool val;
+ nsresult rv = prefs->GetBoolPref(pref, &val);
+ if (NS_FAILED(rv))
+ return false;
+
+ return val;
+}
+
+bool
+nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri)
+{
+ nsAutoCString host;
+ PRNetAddr addr;
+
+ if (!TestBoolPref(kNegotiateAuthAllowNonFqdn))
+ return false;
+
+ if (NS_FAILED(uri->GetAsciiHost(host)))
+ return false;
+
+ // return true if host does not contain a dot and is not an ip address
+ return !host.IsEmpty() && !host.Contains('.') &&
+ PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
+}
+
+bool
+nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
+{
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (!prefs)
+ return false;
+
+ nsAutoCString scheme, host;
+ int32_t port;
+
+ if (NS_FAILED(uri->GetScheme(scheme)))
+ return false;
+ if (NS_FAILED(uri->GetAsciiHost(host)))
+ return false;
+ if (NS_FAILED(uri->GetPort(&port)))
+ return false;
+
+ char *hostList;
+ if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
+ return false;
+
+ // pseudo-BNF
+ // ----------
+ //
+ // url-list base-url ( base-url "," LWS )*
+ // base-url ( scheme-part | host-part | scheme-part host-part )
+ // scheme-part scheme "://"
+ // host-part host [":" port]
+ //
+ // for example:
+ // "https://, http://office.foo.com"
+ //
+
+ char *start = hostList, *end;
+ for (;;) {
+ // skip past any whitespace
+ while (*start == ' ' || *start == '\t')
+ ++start;
+ end = strchr(start, ',');
+ if (!end)
+ end = start + strlen(start);
+ if (start == end)
+ break;
+ if (MatchesBaseURI(scheme, host, port, start, end))
+ return true;
+ if (*end == '\0')
+ break;
+ start = end + 1;
+ }
+
+ free(hostList);
+ return false;
+}
+
+bool
+nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
+ const nsCSubstring &matchHost,
+ int32_t matchPort,
+ const char *baseStart,
+ const char *baseEnd)
+{
+ // check if scheme://host:port matches baseURI
+
+ // parse the base URI
+ const char *hostStart, *schemeEnd = strstr(baseStart, "://");
+ if (schemeEnd) {
+ // the given scheme must match the parsed scheme exactly
+ if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
+ return false;
+ hostStart = schemeEnd + 3;
+ }
+ else
+ hostStart = baseStart;
+
+ // XXX this does not work for IPv6-literals
+ const char *hostEnd = strchr(hostStart, ':');
+ if (hostEnd && hostEnd < baseEnd) {
+ // the given port must match the parsed port exactly
+ int port = atoi(hostEnd + 1);
+ if (matchPort != (int32_t) port)
+ return false;
+ }
+ else
+ hostEnd = baseEnd;
+
+
+ // if we didn't parse out a host, then assume we got a match.
+ if (hostStart == hostEnd)
+ return true;
+
+ uint32_t hostLen = hostEnd - hostStart;
+
+ // matchHost must either equal host or be a subdomain of host
+ if (matchHost.Length() < hostLen)
+ return false;
+
+ const char *end = matchHost.EndReading();
+ if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
+ // if matchHost ends with host from the base URI, then make sure it is
+ // either an exact match, or prefixed with a dot. we don't want
+ // "foobar.com" to match "bar.com"
+ if (matchHost.Length() == hostLen ||
+ *(end - hostLen) == '.' ||
+ *(end - hostLen - 1) == '.')
+ return true;
+ }
+
+ return false;
+}
diff --git a/extensions/auth/nsHttpNegotiateAuth.h b/extensions/auth/nsHttpNegotiateAuth.h
new file mode 100644
index 000000000..4c84616d8
--- /dev/null
+++ b/extensions/auth/nsHttpNegotiateAuth.h
@@ -0,0 +1,44 @@
+/* vim:set ts=4 sw=4 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsHttpNegotiateAuth_h__
+#define nsHttpNegotiateAuth_h__
+
+#include "nsIHttpAuthenticator.h"
+#include "nsIURI.h"
+#include "nsSubstring.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/LazyIdleThread.h"
+
+// The nsHttpNegotiateAuth class provides responses for the GSS-API Negotiate method
+// as specified by Microsoft in draft-brezak-spnego-http-04.txt
+
+class nsHttpNegotiateAuth final : public nsIHttpAuthenticator
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIHTTPAUTHENTICATOR
+
+private:
+ ~nsHttpNegotiateAuth() {}
+
+ // returns the value of the given boolean pref
+ bool TestBoolPref(const char *pref);
+
+ // tests if the host part of an uri is fully qualified
+ bool TestNonFqdn(nsIURI *uri);
+
+ // returns true if URI is accepted by the list of hosts in the pref
+ bool TestPref(nsIURI *, const char *pref);
+
+ bool MatchesBaseURI(const nsCSubstring &scheme,
+ const nsCSubstring &host,
+ int32_t port,
+ const char *baseStart,
+ const char *baseEnd);
+ // Thread for GenerateCredentialsAsync
+ RefPtr<mozilla::LazyIdleThread> mNegotiateThread;
+};
+#endif /* nsHttpNegotiateAuth_h__ */