summaryrefslogtreecommitdiffstats
path: root/extensions/auth
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /extensions/auth
parentInitial commit. (diff)
downloadfirefox-esr-upstream/115.8.0esr.tar.xz
firefox-esr-upstream/115.8.0esr.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--extensions/auth/gssapi.h792
-rw-r--r--extensions/auth/moz.build39
-rw-r--r--extensions/auth/nsAuth.h23
-rw-r--r--extensions/auth/nsAuthGSSAPI.cpp533
-rw-r--r--extensions/auth/nsAuthGSSAPI.h62
-rw-r--r--extensions/auth/nsAuthSASL.cpp123
-rw-r--r--extensions/auth/nsAuthSASL.h35
-rw-r--r--extensions/auth/nsAuthSSPI.cpp574
-rw-r--r--extensions/auth/nsAuthSSPI.h60
-rw-r--r--extensions/auth/nsAuthSambaNTLM.cpp259
-rw-r--r--extensions/auth/nsAuthSambaNTLM.h51
-rw-r--r--extensions/auth/nsHttpNegotiateAuth.cpp565
-rw-r--r--extensions/auth/nsHttpNegotiateAuth.h35
-rw-r--r--extensions/auth/nsIAuthModule.cpp63
14 files changed, 3214 insertions, 0 deletions
diff --git a/extensions/auth/gssapi.h b/extensions/auth/gssapi.h
new file mode 100644
index 0000000000..826d69df79
--- /dev/null
+++ b/extensions/auth/gssapi.h
@@ -0,0 +1,792 @@
+/* vim:set ts=4 sw=2 sts=2 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) && !defined(__aarch64__)
+# 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) && !defined(__aarch64__)
+# 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 0000000000..ac2a23fcf0
--- /dev/null
+++ b/extensions/auth/moz.build
@@ -0,0 +1,39 @@
+# -*- 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 += [
+ "nsAuthGSSAPI.cpp",
+]
+
+SOURCES += [
+ "nsAuthSASL.cpp",
+ "nsHttpNegotiateAuth.cpp", # contains constants whose names conflict with constants in other files
+ "nsIAuthModule.cpp", # includes windows.h recursively which conflicts with TimeStamp.h
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ SOURCES += [
+ "nsAuthSSPI.cpp",
+ ]
+ DEFINES["USE_SSPI"] = True
+else:
+ UNIFIED_SOURCES += [
+ "nsAuthSambaNTLM.cpp",
+ ]
+
+LOCAL_INCLUDES += [
+ "/netwerk/dns", # For nsDNSService2.h
+ "/security/manager/ssl",
+]
+
+FINAL_LIBRARY = "xul"
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Networking")
+
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/extensions/auth/nsAuth.h b/extensions/auth/nsAuth.h
new file mode 100644
index 0000000000..254b33000b
--- /dev/null
+++ b/extensions/auth/nsAuth.h
@@ -0,0 +1,23 @@
+/* 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/nsAuthGSSAPI.cpp b/extensions/auth/nsAuthGSSAPI.cpp
new file mode 100644
index 0000000000..fc3c1592f4
--- /dev/null
+++ b/extensions/auth/nsAuthGSSAPI.cpp
@@ -0,0 +1,533 @@
+/* vim:set ts=4 sw=2 sts=2 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 "mozilla/IntegerPrintfMacros.h"
+
+#include "nsCOMPtr.h"
+#include "nsNativeCharsetUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SharedLibrary.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() {
+#ifdef XP_WIN
+ nsAutoString libPathU;
+ Preferences::GetString(kNegotiateAuthGssLib, libPathU);
+ NS_ConvertUTF16toUTF8 libPath(libPathU);
+#else
+ nsAutoCString libPath;
+ Preferences::GetCString(kNegotiateAuthGssLib, libPath);
+#endif
+ gssNativeImp = Preferences::GetBool(kNegotiateAuthNativeImp);
+
+ PRLibrary* lib = nullptr;
+
+ if (!libPath.IsEmpty()) {
+ LOG(("Attempting to load user specified library [%s]\n", libPath.get()));
+ gssNativeImp = false;
+#ifdef XP_WIN
+ lib = LoadLibraryWithFlags(libPathU.get());
+#else
+ lib = LoadLibraryWithFlags(libPath.get());
+#endif
+ } else {
+#ifdef XP_WIN
+# ifdef _WIN64
+ constexpr auto kLibName = u"gssapi64.dll"_ns;
+# else
+ constexpr auto kLibName = u"gssapi32.dll"_ns;
+# endif
+
+ lib = LoadLibraryWithFlags(kLibName.get());
+#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 (auto& gssFunc : gssFuncs) {
+ gssFunc.func = PR_FindFunctionSymbol(lib, gssFunc.str);
+ if (!gssFunc.func) {
+ LOG(("Fail to load %s function from gssapi library\n", gssFunc.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 nsACString& serviceName, uint32_t serviceFlags,
+ const nsAString& domain, const nsAString& username,
+ const nsAString& password) {
+ // we don't expect to be passed any user credentials
+ NS_ASSERTION(domain.IsEmpty() && username.IsEmpty() && password.IsEmpty(),
+ "unexpected credentials");
+
+ // it's critial that the caller supply a service name to be used
+ NS_ENSURE_TRUE(!serviceName.IsEmpty(), 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 = moz_xmemdup(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=%" PRIx32 "]",
+ static_cast<uint32_t>(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 = moz_xmemdup(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 = moz_xmemdup(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 0000000000..c25c75b294
--- /dev/null
+++ b/extensions/auth/nsAuthGSSAPI.h
@@ -0,0 +1,62 @@
+/* vim:set ts=4 sw=2 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 0000000000..9fcb2859a7
--- /dev/null
+++ b/extensions/auth/nsAuthSASL.cpp
@@ -0,0 +1,123 @@
+/* vim:set ts=4 sw=2 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 "nsNativeCharsetUtils.h"
+#include "nsIPrefService.h"
+#include "nsServiceManagerUtils.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 nsACString& serviceName, uint32_t serviceFlags,
+ const nsAString& domain, const nsAString& username,
+ const nsAString& password) {
+ nsresult rv;
+
+ NS_ASSERTION(!username.IsEmpty(), "SASL requires a username");
+ NS_ASSERTION(domain.IsEmpty() && password.IsEmpty(),
+ "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* authType = "kerb-gss";
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ bool val;
+ rv = prefs->GetBoolPref(kNegotiateAuthSSPI, &val);
+ if (NS_SUCCEEDED(rv) && val) authType = "kerb-sspi";
+ }
+
+ MOZ_ALWAYS_TRUE(mInnerModule = nsIAuthModule::CreateInstance(authType));
+
+ mInnerModule->Init(serviceName, serviceFlags, u""_ns, u""_ns, u""_ns);
+
+ 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);
+ 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 0000000000..f829fd0d17
--- /dev/null
+++ b/extensions/auth/nsAuthSASL.h
@@ -0,0 +1,35 @@
+/* vim:set ts=4 sw=2 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"
+
+/* 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 0000000000..ce3ecebdcd
--- /dev/null
+++ b/extensions/auth/nsAuthSSPI.cpp
@@ -0,0 +1,574 @@
+/* vim:set ts=4 sw=2 sts=2 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 "nsComponentManagerUtils.h"
+#include "nsDNSService2.h"
+#include "nsIDNSService.h"
+#include "nsIDNSRecord.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.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;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult nsAuthSSPI::MakeSN(const nsACString& 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> dnsService =
+ do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ auto dns = static_cast<nsDNSService*>(dnsService.get());
+
+ // 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;
+ mozilla::OriginAttributes attrs;
+ rv = dns->DeprecatedSyncResolve(Substring(buf, index + 1),
+ nsIDNSService::RESOLVE_CANONICAL_NAME, attrs,
+ getter_AddRefs(record));
+ if (NS_FAILED(rv)) return rv;
+ nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(record);
+ if (!rec) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoCString cname;
+ rv = rec->GetCanonicalName(cname);
+ if (NS_SUCCEEDED(rv)) {
+ result = StringHead(buf, index) + "/"_ns + 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) {
+ (sspi->FreeCredentialsHandle)(&mCred);
+ 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 nsACString& aServiceName, uint32_t aServiceFlags,
+ const nsAString& aDomain, const nsAString& aUsername,
+ const nsAString& aPassword) {
+ 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(!aServiceName.IsEmpty(), 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 = aServiceName;
+ 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(aServiceName, mServiceName);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ mServiceFlags = aServiceFlags;
+
+ 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 (!aUsername.IsEmpty() && !aPassword.IsEmpty()) {
+ // Keep a copy of these strings for the duration
+ mUsername = aUsername;
+ mPassword = aPassword;
+ mDomain = aDomain;
+ 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,
+ aServiceFlags & 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);
+ 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);
+
+ // 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);
+ 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=%ld:%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);
+
+ 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 = moz_xmemdup(ib[1].pvBuffer, ib[1].cbBuffer);
+ free(ib[0].pvBuffer);
+ }
+ *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);
+
+ // APP Data
+ bufs.ib[1].BufferType = SECBUFFER_DATA;
+ bufs.ib[1].pvBuffer = moz_xmalloc(inTokenLen);
+ bufs.ib[1].cbBuffer = inTokenLen;
+
+ 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);
+
+ 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);
+
+ *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 0000000000..96ad6b1cb9
--- /dev/null
+++ b/extensions/auth/nsAuthSSPI.h
@@ -0,0 +1,60 @@
+/* vim:set ts=4 sw=2 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
+
+ explicit nsAuthSSPI(pType package = PACKAGE_TYPE_NEGOTIATE);
+
+ private:
+ ~nsAuthSSPI();
+
+ void Reset();
+
+ typedef ::TimeStamp MS_TimeStamp;
+
+ private:
+ nsresult MakeSN(const nsACString& principal, nsCString& result);
+
+ 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 0000000000..5b701f2379
--- /dev/null
+++ b/extensions/auth/nsAuthSambaNTLM.cpp
@@ -0,0 +1,259 @@
+/* vim:set ts=4 sw=2 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 "nspr.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();
+ PR_Free(mInitialMessage);
+}
+
+void nsAuthSambaNTLM::Shutdown() {
+ if (mFromChildFD) {
+ PR_Close(mFromChildFD);
+ mFromChildFD = nullptr;
+ }
+ if (mToChildFD) {
+ PR_Close(mToChildFD);
+ mToChildFD = nullptr;
+ }
+ if (mChildPID) {
+ PR_KillProcess(mChildPID);
+ 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, "YR\n"_ns)) return NS_ERROR_FAILURE;
+ nsCString line;
+ if (!ReadLine(mFromChildFD, line)) return NS_ERROR_FAILURE;
+ if (!StringBeginsWith(line, "YR "_ns)) {
+ // 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 nsACString& serviceName, uint32_t serviceFlags,
+ const nsAString& domain, const nsAString& username,
+ const nsAString& password) {
+ NS_ASSERTION(username.IsEmpty() && domain.IsEmpty() && password.IsEmpty(),
+ "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 = moz_xmemdup(mInitialMessage, mInitialMessageLen);
+ *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);
+ PR_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, "KK "_ns) && !StringBeginsWith(line, "AF "_ns)) {
+ // 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 = moz_xmemdup(buf, *outTokenLen);
+ PR_Free(buf);
+
+ // 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 0000000000..4cfd6a003d
--- /dev/null
+++ b/extensions/auth/nsAuthSambaNTLM.h
@@ -0,0 +1,51 @@
+/* vim:set ts=4 sw=2 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 0000000000..a3ec224b59
--- /dev/null
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -0,0 +1,565 @@
+/* vim:set ts=4 sw=2 sts=2 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 <stdlib.h>
+
+#include "nsAuth.h"
+#include "nsHttpNegotiateAuth.h"
+
+#include "nsIHttpAuthenticableChannel.h"
+#include "nsIAuthModule.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIProxyInfo.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsNetCID.h"
+#include "nsProxyRelease.h"
+#include "plbase64.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Sprintf.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "nsIHttpAuthenticatorCallback.h"
+#include "nsICancelable.h"
+#include "mozilla/net/HttpAuthUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/StaticPrefs_browser.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";
+
+mozilla::StaticRefPtr<nsHttpNegotiateAuth> nsHttpNegotiateAuth::gSingleton;
+
+#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.
+ if (mozilla::StaticPrefs::browser_privatebrowsing_autostart()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+already_AddRefed<nsIHttpAuthenticator> nsHttpNegotiateAuth::GetOrCreate() {
+ nsCOMPtr<nsIHttpAuthenticator> authenticator;
+ if (gSingleton) {
+ authenticator = gSingleton;
+ } else {
+ gSingleton = new nsHttpNegotiateAuth();
+ mozilla::ClearOnShutdown(&gSingleton);
+ authenticator = gSingleton;
+ }
+
+ return authenticator.forget();
+}
+
+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 nsACString& challenge,
+ bool isProxyAuth,
+ nsISupports** sessionState,
+ nsISupports** continuationState,
+ bool* identityInvalid) {
+ nsIAuthModule* rawModule = (nsIAuthModule*)*continuationState;
+
+ *identityInvalid = false;
+ if (rawModule) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIAuthModule> module;
+
+ 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) || mozilla::net::auth::URIMatchesPrefPattern(
+ uri, kNegotiateAuthTrustedURIs));
+ if (!allowed) {
+ LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
+ return NS_ERROR_ABORT;
+ }
+
+ bool delegation = mozilla::net::auth::URIMatchesPrefPattern(
+ 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.InsertLiteral("HTTP@", 0);
+
+ const char* authType;
+ if (TestBoolPref(kNegotiateAuthSSPI)) {
+ LOG((" using negotiate-sspi\n"));
+ authType = "negotiate-sspi";
+ } else {
+ LOG((" using negotiate-gss\n"));
+ authType = "negotiate-gss";
+ }
+
+ MOZ_ALWAYS_TRUE(module = nsIAuthModule::CreateInstance(authType));
+
+ rv = module->Init(service, req_flags, u""_ns, u""_ns, u""_ns);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ module.forget(continuationState);
+ 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 {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit GetNextTokenCompleteEvent(nsIHttpAuthenticatorCallback* aCallback)
+ : mCallback(aCallback) {}
+
+ nsresult DispatchSuccess(const nsACString& 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);
+ }
+
+ nsresult 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;
+ nsCOMPtr<nsIHttpAuthenticatorCallback> callback = std::move(mCallback);
+ if (callback) {
+ callback->OnCredsGenerated(mCreds, mFlags, aReason, nullptr, nullptr);
+ }
+ return NS_OK;
+ }
+
+ private:
+ virtual ~GetNextTokenCompleteEvent() = default;
+
+ nsCOMPtr<nsIHttpAuthenticatorCallback> mCallback;
+ nsCString mCreds;
+ uint32_t mFlags = 0;
+ nsresult mResult = NS_OK;
+ bool mCancelled = false;
+ nsCOMPtr<nsISupports> mSessionState;
+ nsCOMPtr<nsISupports> mContinuationState;
+};
+
+inline nsISupports* ToSupports(GetNextTokenCompleteEvent* aEvent) {
+ return static_cast<nsIRunnable*>(aEvent);
+}
+
+NS_IMPL_ISUPPORTS(GetNextTokenCompleteEvent, nsIRunnable, nsICancelable)
+
+//
+// GetNextTokenRunnable
+//
+// This runnable is created by GenerateCredentialsAsync and it runs
+// on the background thread pool and calls GenerateCredentials.
+//
+class GetNextTokenRunnable final : public mozilla::Runnable {
+ ~GetNextTokenRunnable() override = default;
+
+ public:
+ GetNextTokenRunnable(
+ nsMainThreadPtrHandle<nsIHttpAuthenticableChannel>& authChannel,
+ const nsACString& challenge, bool isProxyAuth, const nsAString& domain,
+ const nsAString& username, const nsAString& password,
+ nsISupports* sessionState, nsISupports* continuationState,
+ nsMainThreadPtrHandle<GetNextTokenCompleteEvent>& aCompleteEvent)
+ : mozilla::Runnable("GetNextTokenRunnable"),
+ 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());
+
+ nsCString 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(nsCString& aCreds, uint32_t* aFlags) {
+ nsresult rv;
+
+ // Use negotiate service to call GenerateCredentials outside of main thread
+ nsCOMPtr<nsIHttpAuthenticator> authenticator = new nsHttpNegotiateAuth();
+
+ 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, mIsProxyAuth, mDomain, mUsername, mPassword,
+ &sessionState, &continuationState, aFlags, aCreds);
+ if (mSessionState != sessionState) {
+ mSessionState = sessionState;
+ }
+ if (mContinuationState != continuationState) {
+ mContinuationState = continuationState;
+ }
+ return rv;
+ }
+
+ private:
+ nsMainThreadPtrHandle<nsIHttpAuthenticableChannel> mAuthChannel;
+ nsCString mChallenge;
+ bool mIsProxyAuth;
+ nsString mDomain;
+ nsString mUsername;
+ nsString mPassword;
+ nsCOMPtr<nsISupports> mSessionState;
+ nsCOMPtr<nsISupports> mContinuationState;
+ nsMainThreadPtrHandle<GetNextTokenCompleteEvent> mCompleteEvent;
+};
+
+} // anonymous namespace
+
+NS_IMETHODIMP
+nsHttpNegotiateAuth::GenerateCredentialsAsync(
+ nsIHttpAuthenticableChannel* authChannel,
+ nsIHttpAuthenticatorCallback* aCallback, const nsACString& challenge,
+ bool isProxyAuth, const nsAString& domain, const nsAString& username,
+ const nsAString& password, nsISupports* sessionState,
+ nsISupports* continuationState, nsICancelable** aCancelable) {
+ NS_ENSURE_ARG(aCallback);
+ NS_ENSURE_ARG_POINTER(aCancelable);
+
+ nsMainThreadPtrHandle<nsIHttpAuthenticableChannel> handle(
+ new nsMainThreadPtrHolder<nsIHttpAuthenticableChannel>(
+ "nsIHttpAuthenticableChannel", authChannel, false));
+ nsMainThreadPtrHandle<GetNextTokenCompleteEvent> cancelEvent(
+ new nsMainThreadPtrHolder<GetNextTokenCompleteEvent>(
+ "GetNextTokenCompleteEvent", new GetNextTokenCompleteEvent(aCallback),
+ false));
+ nsCOMPtr<nsIRunnable> getNextTokenRunnable = new GetNextTokenRunnable(
+ handle, challenge, isProxyAuth, domain, username, password, sessionState,
+ continuationState, cancelEvent);
+
+ nsresult rv = NS_DispatchBackgroundTask(
+ getNextTokenRunnable, nsIEventTarget::DISPATCH_EVENT_MAY_BLOCK);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<GetNextTokenCompleteEvent> cancelable(cancelEvent.get());
+ cancelable.forget(aCancelable);
+ 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 nsACString& aChallenge,
+ bool isProxyAuth, const nsAString& domain, const nsAString& username,
+ const nsAString& password, nsISupports** sessionState,
+ nsISupports** continuationState, uint32_t* flags, nsACString& 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",
+ aChallenge.BeginReading()));
+
+#ifdef DEBUG
+ bool isGssapiAuth = StringBeginsWith(aChallenge, "Negotiate"_ns,
+ nsCaseInsensitiveCStringComparator);
+ 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.
+ //
+
+ nsAutoCString inToken;
+ if (aChallenge.Length() > kNegotiateLen) {
+ nsDependentCSubstring challenge(aChallenge, kNegotiateLen);
+ uint32_t startPos = 0;
+ while (startPos < challenge.Length() && challenge[startPos] == ' ') {
+ startPos++;
+ }
+ if (startPos == challenge.Length()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // strip off any padding (see bug 230351)
+ uint32_t len = challenge.Length();
+ while (len > startPos && challenge[len - 1] == '=') {
+ len--;
+ }
+
+ //
+ // Decode the response that followed the "Negotiate" token
+ //
+ (void)Base64Decode(
+ nsDependentCSubstring(challenge, startPos, len - startPos), inToken);
+ }
+
+ void* outToken = nullptr;
+ uint32_t outTokenLen = 0;
+ nsresult rv = module->GetNextToken(inToken.get(), inToken.Length(), &outToken,
+ &outTokenLen);
+ if (NS_FAILED(rv)) {
+ if (outToken) {
+ // Technically if the call fails we shouln't have allocated, but
+ // Coverity doesn't know that.
+ free(outToken);
+ }
+ return rv;
+ }
+
+ if (outTokenLen == 0) {
+ LOG((" No output token to send, exiting"));
+ return NS_ERROR_FAILURE;
+ }
+
+ //
+ // base64 encode the output token.
+ //
+ nsAutoCString encodedToken;
+ rv = mozilla::Base64Encode(
+ nsDependentCSubstring((char*)outToken, outTokenLen), encodedToken);
+ free(outToken);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ LOG((" Sending a token of length %d\n", outTokenLen));
+
+ creds = nsPrintfCString("%s %s", kNegotiate, encodedToken.get());
+ 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;
+
+ 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('.') &&
+ !mozilla::net::HostIsIPLiteral(host);
+}
diff --git a/extensions/auth/nsHttpNegotiateAuth.h b/extensions/auth/nsHttpNegotiateAuth.h
new file mode 100644
index 0000000000..d84f04df5a
--- /dev/null
+++ b/extensions/auth/nsHttpNegotiateAuth.h
@@ -0,0 +1,35 @@
+/* vim:set ts=4 sw=2 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 "mozilla/StaticPtr.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
+
+ static already_AddRefed<nsIHttpAuthenticator> GetOrCreate();
+
+ 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);
+
+ // Singleton pointer
+ static mozilla::StaticRefPtr<nsHttpNegotiateAuth> gSingleton;
+};
+#endif /* nsHttpNegotiateAuth_h__ */
diff --git a/extensions/auth/nsIAuthModule.cpp b/extensions/auth/nsIAuthModule.cpp
new file mode 100644
index 0000000000..8f18a344eb
--- /dev/null
+++ b/extensions/auth/nsIAuthModule.cpp
@@ -0,0 +1,63 @@
+/* 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 "nsIAuthModule.h"
+#if defined(USE_SSPI)
+# include "nsAuthSSPI.h"
+#else
+# include "nsAuthSambaNTLM.h"
+#endif
+#include "nsCRT.h"
+#include "nsAuthGSSAPI.h"
+#include "nsAuthSASL.h"
+#include "nsNTLMAuthModule.h"
+#include "nsNSSComponent.h"
+
+// static
+already_AddRefed<nsIAuthModule> nsIAuthModule::CreateInstance(
+ const char* aType) {
+ nsCOMPtr<nsIAuthModule> auth;
+ if (!nsCRT::strcmp(aType, "kerb-gss")) {
+ auth = new nsAuthGSSAPI(PACKAGE_TYPE_KERBEROS);
+ } else if (!nsCRT::strcmp(aType, "negotiate-gss")) {
+ auth = new nsAuthGSSAPI(PACKAGE_TYPE_NEGOTIATE);
+#if defined(USE_SSPI)
+ } else if (!nsCRT::strcmp(aType, "negotiate-sspi")) {
+ auth = new nsAuthSSPI();
+ } else if (!nsCRT::strcmp(aType, "kerb-sspi")) {
+ auth = new nsAuthSSPI(PACKAGE_TYPE_KERBEROS);
+ } else if (!nsCRT::strcmp(aType, "sys-ntlm")) {
+ auth = new nsAuthSSPI(PACKAGE_TYPE_NTLM);
+#elif !defined(XP_MACOSX)
+ } else if (!nsCRT::strcmp(aType, "sys-ntlm")) {
+ RefPtr<nsAuthSambaNTLM> sambaAuth = new nsAuthSambaNTLM();
+
+ nsresult rv = sambaAuth->SpawnNTLMAuthHelper();
+ if (NS_FAILED(rv)) {
+ // Failure here probably means that cached credentials were not available
+ return nullptr;
+ }
+
+ auth = std::move(sambaAuth);
+#endif
+ } else if (!nsCRT::strcmp(aType, "sasl-gssapi")) {
+ auth = new nsAuthSASL();
+ } else if (!nsCRT::strcmp(aType, "ntlm") && XRE_IsParentProcess() &&
+ EnsureNSSInitializedChromeOrContent()) {
+ RefPtr<nsNTLMAuthModule> ntlmAuth = new nsNTLMAuthModule();
+
+ nsresult rv = ntlmAuth->InitTest();
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ auth = std::move(ntlmAuth);
+ } else {
+ return nullptr;
+ }
+
+ return auth.forget();
+}
+
+mozilla::LazyLogModule gNegotiateLog("negotiateauth");