import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

848
extensions/auth/gssapi.h Normal file
View file

@ -0,0 +1,848 @@
/* vim:set ts=4 sw=4 sts=4 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Copyright 1993 by OpenVision Technologies, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appears in all copies and
* that both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of OpenVision not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. OpenVision makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
****** END LICENSE BLOCK ***** */
#ifndef GSSAPI_H_
#define GSSAPI_H_
/*
* Also define _GSSAPI_H_ as that is what the Kerberos 5 code defines and
* what header files on some systems look for.
*/
#define _GSSAPI_H_
/*
* On Mac OS X, Kerberos/Kerberos.h is used to gain access to certain
* system-specific Kerberos functions, but on 10.4, that file also brings
* in other headers that conflict with this one.
*/
#define _GSSAPI_GENERIC_H_
#define _GSSAPI_KRB5_H_
/*
* Define windows specific needed parameters.
*/
#ifndef GSS_CALLCONV
#if defined(_WIN32)
#define GSS_CALLCONV __stdcall
#define GSS_CALLCONV_C __cdecl
#else
#define GSS_CALLCONV
#define GSS_CALLCONV_C
#endif
#endif /* GSS_CALLCONV */
#ifdef GSS_USE_FUNCTION_POINTERS
#ifdef _WIN32
#undef GSS_CALLCONV
#define GSS_CALLCONV
#define GSS_FUNC(f) (__stdcall *f##_type)
#else
#define GSS_FUNC(f) (*f##_type)
#endif
#define GSS_MAKE_TYPEDEF typedef
#else
#define GSS_FUNC(f) f
#define GSS_MAKE_TYPEDEF
#endif
/*
* First, include stddef.h to get size_t defined.
*/
#include <stddef.h>
/*
* Configure set the following
*/
#ifndef SIZEOF_LONG
#undef SIZEOF_LONG
#endif
#ifndef SIZEOF_SHORT
#undef SIZEOF_SHORT
#endif
#ifndef EXTERN_C_BEGIN
#ifdef __cplusplus
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif
#endif
EXTERN_C_BEGIN
#if defined(XP_MACOSX)
# pragma pack(push,2)
#endif
/*
* If the platform supports the xom.h header file, it should be
* included here.
*/
/* #include <xom.h> */
/*
* Now define the three implementation-dependent types.
*/
typedef void * gss_name_t ;
typedef void * gss_ctx_id_t ;
typedef void * gss_cred_id_t ;
/*
* The following type must be defined as the smallest natural
* unsigned integer supported by the platform that has at least
* 32 bits of precision.
*/
#if SIZEOF_LONG == 4
typedef unsigned long gss_uint32;
#elif SIZEOF_SHORT == 4
typedef unsigned short gss_uint32;
#else
typedef unsigned int gss_uint32;
#endif
#ifdef OM_STRING
/*
* We have included the xom.h header file. Verify that OM_uint32
* is defined correctly.
*/
#if sizeof(gss_uint32) != sizeof(OM_uint32)
#error Incompatible definition of OM_uint32 from xom.h
#endif
typedef OM_object_identifier gss_OID_desc, *gss_OID;
#else /* !OM_STRING */
/*
* We can't use X/Open definitions, so roll our own.
*/
typedef gss_uint32 OM_uint32;
typedef struct gss_OID_desc_struct {
OM_uint32 length;
void *elements;
} gss_OID_desc, *gss_OID;
#endif /* !OM_STRING */
typedef struct gss_OID_set_desc_struct {
size_t count;
gss_OID elements;
} gss_OID_set_desc, *gss_OID_set;
/*
* For now, define a QOP-type as an OM_uint32
*/
typedef OM_uint32 gss_qop_t;
typedef int gss_cred_usage_t;
typedef struct gss_buffer_desc_struct {
size_t length;
void *value;
} gss_buffer_desc, *gss_buffer_t;
typedef struct gss_channel_bindings_struct {
OM_uint32 initiator_addrtype;
gss_buffer_desc initiator_address;
OM_uint32 acceptor_addrtype;
gss_buffer_desc acceptor_address;
gss_buffer_desc application_data;
} *gss_channel_bindings_t;
/*
* Flag bits for context-level services.
*/
#define GSS_C_DELEG_FLAG 1
#define GSS_C_MUTUAL_FLAG 2
#define GSS_C_REPLAY_FLAG 4
#define GSS_C_SEQUENCE_FLAG 8
#define GSS_C_CONF_FLAG 16
#define GSS_C_INTEG_FLAG 32
#define GSS_C_ANON_FLAG 64
#define GSS_C_PROT_READY_FLAG 128
#define GSS_C_TRANS_FLAG 256
/*
* Credential usage options
*/
#define GSS_C_BOTH 0
#define GSS_C_INITIATE 1
#define GSS_C_ACCEPT 2
/*
* Status code types for gss_display_status
*/
#define GSS_C_GSS_CODE 1
#define GSS_C_MECH_CODE 2
/*
* The constant definitions for channel-bindings address families
*/
#define GSS_C_AF_UNSPEC 0
#define GSS_C_AF_LOCAL 1
#define GSS_C_AF_INET 2
#define GSS_C_AF_IMPLINK 3
#define GSS_C_AF_PUP 4
#define GSS_C_AF_CHAOS 5
#define GSS_C_AF_NS 6
#define GSS_C_AF_NBS 7
#define GSS_C_AF_ECMA 8
#define GSS_C_AF_DATAKIT 9
#define GSS_C_AF_CCITT 10
#define GSS_C_AF_SNA 11
#define GSS_C_AF_DECnet 12
#define GSS_C_AF_DLI 13
#define GSS_C_AF_LAT 14
#define GSS_C_AF_HYLINK 15
#define GSS_C_AF_APPLETALK 16
#define GSS_C_AF_BSC 17
#define GSS_C_AF_DSS 18
#define GSS_C_AF_OSI 19
#define GSS_C_AF_X25 21
#define GSS_C_AF_NULLADDR 255
/*
* Various Null values
*/
#define GSS_C_NO_NAME ((gss_name_t) 0)
#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
#define GSS_C_NO_OID ((gss_OID) 0)
#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
#define GSS_C_EMPTY_BUFFER {0, nullptr}
/*
* Some alternate names for a couple of the above
* values. These are defined for V1 compatibility.
*/
#define GSS_C_NULL_OID GSS_C_NO_OID
#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
/*
* Define the default Quality of Protection for per-message
* services. Note that an implementation that offers multiple
* levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
* (as done here) to mean "default protection", or to a specific
* explicit QOP value. However, a value of 0 should always be
* interpreted by a GSSAPI implementation as a request for the
* default protection level.
*/
#define GSS_C_QOP_DEFAULT 0
/*
* Expiration time of 2^32-1 seconds means infinite lifetime for a
* credential or security context
*/
#define GSS_C_INDEFINITE 0xfffffffful
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
* "\x01\x02\x01\x01"},
* corresponding to an object-identifier value of
* {iso(1) member-body(2) United States(840) mit(113554)
* infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
* GSS_C_NT_USER_NAME should be initialized to point
* to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_USER_NAME;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
* "\x01\x02\x01\x02"},
* corresponding to an object-identifier value of
* {iso(1) member-body(2) United States(840) mit(113554)
* infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
* The constant GSS_C_NT_MACHINE_UID_NAME should be
* initialized to point to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_MACHINE_UID_NAME;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
* "\x01\x02\x01\x03"},
* corresponding to an object-identifier value of
* {iso(1) member-body(2) United States(840) mit(113554)
* infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
* The constant GSS_C_NT_STRING_UID_NAME should be
* initialized to point to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_STRING_UID_NAME;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
* corresponding to an object-identifier value of
* {iso(1) org(3) dod(6) internet(1) security(5)
* nametypes(6) gss-host-based-services(2)). The constant
* GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
* to that gss_OID_desc. This is a deprecated OID value, and
* implementations wishing to support hostbased-service names
* should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
* defined below, to identify such names;
* GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
* for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
* parameter, but should not be emitted by GSSAPI
* implementations
*/
extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
* "\x01\x02\x01\x04"}, corresponding to an
* object-identifier value of {iso(1) member-body(2)
* Unites States(840) mit(113554) infosys(1) gssapi(2)
* generic(1) service_name(4)}. The constant
* GSS_C_NT_HOSTBASED_SERVICE should be initialized
* to point to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_HOSTBASED_SERVICE;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {6, (void *)"\x2b\x06\01\x05\x06\x03"},
* corresponding to an object identifier value of
* {1(iso), 3(org), 6(dod), 1(internet), 5(security),
* 6(nametypes), 3(gss-anonymous-name)}. The constant
* and GSS_C_NT_ANONYMOUS should be initialized to point
* to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_ANONYMOUS;
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
* corresponding to an object-identifier value of
* {1(iso), 3(org), 6(dod), 1(internet), 5(security),
* 6(nametypes), 4(gss-api-exported-name)}. The constant
* GSS_C_NT_EXPORT_NAME should be initialized to point
* to that gss_OID_desc.
*/
extern gss_OID GSS_C_NT_EXPORT_NAME;
/* Major status codes */
#define GSS_S_COMPLETE 0
/*
* Some "helper" definitions to make the status code macros obvious.
*/
#define GSS_C_CALLING_ERROR_OFFSET 24
#define GSS_C_ROUTINE_ERROR_OFFSET 16
#define GSS_C_SUPPLEMENTARY_OFFSET 0
#define GSS_C_CALLING_ERROR_MASK 0377ul
#define GSS_C_ROUTINE_ERROR_MASK 0377ul
#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
/*
* The macros that test status codes for error conditions.
* Note that the GSS_ERROR() macro has changed slightly from
* the V1 GSSAPI so that it now evaluates its argument
* only once.
*/
#define GSS_CALLING_ERROR(x) \
(x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
#define GSS_ROUTINE_ERROR(x) \
(x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
#define GSS_SUPPLEMENTARY_INFO(x) \
(x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
#define GSS_ERROR(x) \
(x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
(GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
/*
* Now the actual status code definitions
*/
/*
* Calling errors:
*/
#define GSS_S_CALL_INACCESSIBLE_READ \
(1ul << GSS_C_CALLING_ERROR_OFFSET)
#define GSS_S_CALL_INACCESSIBLE_WRITE \
(2ul << GSS_C_CALLING_ERROR_OFFSET)
#define GSS_S_CALL_BAD_STRUCTURE \
(3ul << GSS_C_CALLING_ERROR_OFFSET)
/*
* Routine errors:
*/
#define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_MIC GSS_S_BAD_SIG
#define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET)
#define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)
/*
* Supplementary info bits:
*/
#define GSS_S_CONTINUE_NEEDED (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
#define GSS_S_DUPLICATE_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
#define GSS_S_OLD_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
#define GSS_S_UNSEQ_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
#define GSS_S_GAP_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
/*
* Finally, function prototypes for the GSS-API routines.
*/
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_acquire_cred)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* desired_name */
OM_uint32, /* time_req */
const gss_OID_set, /* desired_mechs */
gss_cred_usage_t, /* cred_usage */
gss_cred_id_t *, /* output_cred_handle */
gss_OID_set *, /* actual_mechs */
OM_uint32 * /* time_rec */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_release_cred)
(OM_uint32 *, /* minor_status */
gss_cred_id_t * /* cred_handle */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_init_sec_context)
(OM_uint32 *, /* minor_status */
const gss_cred_id_t, /* initiator_cred_handle */
gss_ctx_id_t *, /* context_handle */
const gss_name_t, /* target_name */
const gss_OID, /* mech_type */
OM_uint32, /* req_flags */
OM_uint32, /* time_req */
const gss_channel_bindings_t, /* input_chan_bindings */
const gss_buffer_t, /* input_token */
gss_OID *, /* actual_mech_type */
gss_buffer_t, /* output_token */
OM_uint32 *, /* ret_flags */
OM_uint32 * /* time_rec */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_accept_sec_context)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t *, /* context_handle */
const gss_cred_id_t, /* acceptor_cred_handle */
const gss_buffer_t, /* input_token_buffer */
const gss_channel_bindings_t, /* input_chan_bindings */
gss_name_t *, /* src_name */
gss_OID *, /* mech_type */
gss_buffer_t, /* output_token */
OM_uint32 *, /* ret_flags */
OM_uint32 *, /* time_rec */
gss_cred_id_t * /* delegated_cred_handle */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_process_context_token)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
const gss_buffer_t /* token_buffer */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_delete_sec_context)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t *, /* context_handle */
gss_buffer_t /* output_token */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_context_time)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
OM_uint32 * /* time_rec */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_get_mic)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
gss_qop_t, /* qop_req */
const gss_buffer_t, /* message_buffer */
gss_buffer_t /* message_token */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_verify_mic)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
const gss_buffer_t, /* message_buffer */
const gss_buffer_t, /* token_buffer */
gss_qop_t * /* qop_state */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_wrap)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
int, /* conf_req_flag */
gss_qop_t, /* qop_req */
const gss_buffer_t, /* input_message_buffer */
int *, /* conf_state */
gss_buffer_t /* output_message_buffer */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_unwrap)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
const gss_buffer_t, /* input_message_buffer */
gss_buffer_t, /* output_message_buffer */
int *, /* conf_state */
gss_qop_t * /* qop_state */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_display_status)
(OM_uint32 *, /* minor_status */
OM_uint32, /* status_value */
int, /* status_type */
const gss_OID, /* mech_type */
OM_uint32 *, /* message_context */
gss_buffer_t /* status_string */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_indicate_mechs)
(OM_uint32 *, /* minor_status */
gss_OID_set * /* mech_set */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_compare_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* name1 */
const gss_name_t, /* name2 */
int * /* name_equal */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_display_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* input_name */
gss_buffer_t, /* output_name_buffer */
gss_OID * /* output_name_type */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_import_name)
(OM_uint32 *, /* minor_status */
const gss_buffer_t, /* input_name_buffer */
const gss_OID, /* input_name_type */
gss_name_t * /* output_name */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_export_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* input_name */
gss_buffer_t /* exported_name */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_release_name)
(OM_uint32 *, /* minor_status */
gss_name_t * /* input_name */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_release_buffer)
(OM_uint32 *, /* minor_status */
gss_buffer_t /* buffer */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_release_oid_set)
(OM_uint32 *, /* minor_status */
gss_OID_set * /* set */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_inquire_cred)
(OM_uint32 *, /* minor_status */
const gss_cred_id_t, /* cred_handle */
gss_name_t *, /* name */
OM_uint32 *, /* lifetime */
gss_cred_usage_t *, /* cred_usage */
gss_OID_set * /* mechanisms */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_inquire_context)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
gss_name_t *, /* src_name */
gss_name_t *, /* targ_name */
OM_uint32 *, /* lifetime_rec */
gss_OID *, /* mech_type */
OM_uint32 *, /* ctx_flags */
int *, /* locally_initiated */
int * /* open */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_wrap_size_limit)
(OM_uint32 *, /* minor_status */
const gss_ctx_id_t, /* context_handle */
int, /* conf_req_flag */
gss_qop_t, /* qop_req */
OM_uint32, /* req_output_size */
OM_uint32 * /* max_input_size */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_add_cred)
(OM_uint32 *, /* minor_status */
const gss_cred_id_t, /* input_cred_handle */
const gss_name_t, /* desired_name */
const gss_OID, /* desired_mech */
gss_cred_usage_t, /* cred_usage */
OM_uint32, /* initiator_time_req */
OM_uint32, /* acceptor_time_req */
gss_cred_id_t *, /* output_cred_handle */
gss_OID_set *, /* actual_mechs */
OM_uint32 *, /* initiator_time_rec */
OM_uint32 * /* acceptor_time_rec */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_inquire_cred_by_mech)
(OM_uint32 *, /* minor_status */
const gss_cred_id_t, /* cred_handle */
const gss_OID, /* mech_type */
gss_name_t *, /* name */
OM_uint32 *, /* initiator_lifetime */
OM_uint32 *, /* acceptor_lifetime */
gss_cred_usage_t * /* cred_usage */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_export_sec_context)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t *, /* context_handle */
gss_buffer_t /* interprocess_token */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_import_sec_context)
(OM_uint32 *, /* minor_status */
const gss_buffer_t, /* interprocess_token */
gss_ctx_id_t * /* context_handle */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_create_empty_oid_set)
(OM_uint32 *, /* minor_status */
gss_OID_set * /* oid_set */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_add_oid_set_member)
(OM_uint32 *, /* minor_status */
const gss_OID, /* member_oid */
gss_OID_set * /* oid_set */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_test_oid_set_member)
(OM_uint32 *, /* minor_status */
const gss_OID, /* member */
const gss_OID_set, /* set */
int * /* present */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_inquire_names_for_mech)
(OM_uint32 *, /* minor_status */
const gss_OID, /* mechanism */
gss_OID_set * /* name_types */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_inquire_mechs_for_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* input_name */
gss_OID_set * /* mech_types */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_canonicalize_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* input_name */
const gss_OID, /* mech_type */
gss_name_t * /* output_name */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_duplicate_name)
(OM_uint32 *, /* minor_status */
const gss_name_t, /* src_name */
gss_name_t * /* dest_name */
);
/*
* The following routines are obsolete variants of gss_get_mic,
* gss_verify_mic, gss_wrap and gss_unwrap. They should be
* provided by GSSAPI V2 implementations for backwards
* compatibility with V1 applications. Distinct entrypoints
* (as opposed to #defines) should be provided, both to allow
* GSSAPI V1 applications to link against GSSAPI V2 implementations,
* and to retain the slight parameter type differences between the
* obsolete versions of these routines and their current forms.
*/
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_sign)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t, /* context_handle */
int, /* qop_req */
gss_buffer_t, /* message_buffer */
gss_buffer_t /* message_token */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_verify)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t, /* context_handle */
gss_buffer_t, /* message_buffer */
gss_buffer_t, /* token_buffer */
int * /* qop_state */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_seal)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t, /* context_handle */
int, /* conf_req_flag */
int, /* qop_req */
gss_buffer_t, /* input_message_buffer */
int *, /* conf_state */
gss_buffer_t /* output_message_buffer */
);
GSS_MAKE_TYPEDEF
OM_uint32
GSS_CALLCONV GSS_FUNC(gss_unseal)
(OM_uint32 *, /* minor_status */
gss_ctx_id_t, /* context_handle */
gss_buffer_t, /* input_message_buffer */
gss_buffer_t, /* output_message_buffer */
int *, /* conf_state */
int * /* qop_state */
);
#if defined(XP_MACOSX)
# pragma pack(pop)
#endif
EXTERN_C_END
#endif /* GSSAPI_H_ */

27
extensions/auth/moz.build Normal file
View file

@ -0,0 +1,27 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'nsAuthFactory.cpp',
'nsAuthGSSAPI.cpp',
]
SOURCES += [
'nsAuthSASL.cpp',
'nsHttpNegotiateAuth.cpp', # contains constants whose names conflict with constants in other files
]
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [
'nsAuthSSPI.cpp',
]
DEFINES['USE_SSPI'] = True
else:
UNIFIED_SOURCES += [
'nsAuthSambaNTLM.cpp',
]
FINAL_LIBRARY = 'xul'

27
extensions/auth/nsAuth.h Normal file
View file

@ -0,0 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAuth_h__
#define nsAuth_h__
/* types of packages */
enum pType {
PACKAGE_TYPE_KERBEROS,
PACKAGE_TYPE_NEGOTIATE,
PACKAGE_TYPE_NTLM
};
#include "mozilla/Logging.h"
//
// in order to do logging, the following environment variables need to be set:
//
// set NSPR_LOG_MODULES=negotiateauth:4
// set NSPR_LOG_FILE=negotiateauth.log
//
extern mozilla::LazyLogModule gNegotiateLog;
#define LOG(args) MOZ_LOG(gNegotiateLog, mozilla::LogLevel::Debug, args)
#endif /* !defined( nsAuth_h__ ) */

View file

@ -0,0 +1,247 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ModuleUtils.h"
#include "nsAuth.h"
//-----------------------------------------------------------------------------
#define NS_HTTPNEGOTIATEAUTH_CID \
{ /* 75c80fd0-accb-432c-af59-ec60668c3990 */ \
0x75c80fd0, \
0xaccb, \
0x432c, \
{0xaf, 0x59, 0xec, 0x60, 0x66, 0x8c, 0x39, 0x90} \
}
#include "nsHttpNegotiateAuth.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNegotiateAuth)
//-----------------------------------------------------------------------------
#define NS_NEGOTIATEAUTH_CID \
{ /* 96ec4163-efc8-407a-8735-007fb26be4e8 */ \
0x96ec4163, \
0xefc8, \
0x407a, \
{0x87, 0x35, 0x00, 0x7f, 0xb2, 0x6b, 0xe4, 0xe8} \
}
#define NS_GSSAUTH_CID \
{ /* dc8e21a0-03e4-11da-8cd6-0800200c9a66 */ \
0xdc8e21a0, \
0x03e4, \
0x11da, \
{0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
}
#include "nsAuthGSSAPI.h"
#if defined( USE_SSPI )
#include "nsAuthSSPI.h"
static nsresult
nsSysNTLMAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsAuthSSPI *auth = new nsAuthSSPI(PACKAGE_TYPE_NTLM);
if (!auth)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(auth);
nsresult rv = auth->QueryInterface(iid, result);
NS_RELEASE(auth);
return rv;
}
static nsresult
nsKerbSSPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsAuthSSPI *auth = new nsAuthSSPI(PACKAGE_TYPE_KERBEROS);
if (!auth)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(auth);
nsresult rv = auth->QueryInterface(iid, result);
NS_RELEASE(auth);
return rv;
}
#define NS_SYSNTLMAUTH_CID \
{ /* dc195987-6e9a-47bc-b1fd-ab895d398833 */ \
0xdc195987, \
0x6e9a, \
0x47bc, \
{0xb1, 0xfd, 0xab, 0x89, 0x5d, 0x39, 0x88, 0x33} \
}
#define NS_NEGOTIATEAUTHSSPI_CID \
{ /* 78d3b0c0-0241-11da-8cd6-0800200c9a66 */ \
0x78d3b0c0, \
0x0241, \
0x11da, \
{0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
}
#define NS_KERBAUTHSSPI_CID \
{ /* 8c3a0e20-03e5-11da-8cd6-0800200c9a66 */ \
0x8c3a0e20, \
0x03e5, \
0x11da, \
{0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \
}
#else
#define NS_SAMBANTLMAUTH_CID \
{ /* bc54f001-6eb0-4e32-9f49-7e064d8e70ef */ \
0xbc54f001, \
0x6eb0, \
0x4e32, \
{0x9f, 0x49, 0x7e, 0x06, 0x4d, 0x8e, 0x70, 0xef} \
}
#include "nsAuthSambaNTLM.h"
static nsresult
nsSambaNTLMAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
if (outer)
return NS_ERROR_NO_AGGREGATION;
RefPtr<nsAuthSambaNTLM> auth = new nsAuthSambaNTLM();
if (!auth)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = auth->SpawnNTLMAuthHelper();
if (NS_FAILED(rv)) {
// Failure here probably means that cached credentials were not available
return rv;
}
return auth->QueryInterface(iid, result);
}
#endif
static nsresult
nsKerbGSSAPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsAuthGSSAPI *auth = new nsAuthGSSAPI(PACKAGE_TYPE_KERBEROS);
if (!auth)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(auth);
nsresult rv = auth->QueryInterface(iid, result);
NS_RELEASE(auth);
return rv;
}
static nsresult
nsGSSAPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsAuthGSSAPI *auth = new nsAuthGSSAPI(PACKAGE_TYPE_NEGOTIATE);
if (!auth)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(auth);
nsresult rv = auth->QueryInterface(iid, result);
NS_RELEASE(auth);
return rv;
}
#if defined( USE_SSPI )
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAuthSSPI)
#endif
#define NS_AUTHSASL_CID \
{ /* 815e42e0-72cc-480f-934b-148e33c228a6 */ \
0x815e42e0, \
0x72cc, \
0x480f, \
{0x93, 0x4b, 0x14, 0x8e, 0x33, 0xc2, 0x28, 0xa6} \
}
#include "nsAuthSASL.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAuthSASL)
NS_DEFINE_NAMED_CID(NS_GSSAUTH_CID);
NS_DEFINE_NAMED_CID(NS_NEGOTIATEAUTH_CID);
#if defined( USE_SSPI )
NS_DEFINE_NAMED_CID(NS_NEGOTIATEAUTHSSPI_CID);
NS_DEFINE_NAMED_CID(NS_KERBAUTHSSPI_CID);
NS_DEFINE_NAMED_CID(NS_SYSNTLMAUTH_CID);
#else
NS_DEFINE_NAMED_CID(NS_SAMBANTLMAUTH_CID);
#endif
NS_DEFINE_NAMED_CID(NS_HTTPNEGOTIATEAUTH_CID);
NS_DEFINE_NAMED_CID(NS_AUTHSASL_CID);
static const mozilla::Module::CIDEntry kAuthCIDs[] = {
{ &kNS_GSSAUTH_CID, false, nullptr, nsKerbGSSAPIAuthConstructor },
{ &kNS_NEGOTIATEAUTH_CID, false, nullptr, nsGSSAPIAuthConstructor },
#if defined( USE_SSPI )
{ &kNS_NEGOTIATEAUTHSSPI_CID, false, nullptr, nsAuthSSPIConstructor },
{ &kNS_KERBAUTHSSPI_CID, false, nullptr, nsKerbSSPIAuthConstructor },
{ &kNS_SYSNTLMAUTH_CID, false, nullptr, nsSysNTLMAuthConstructor },
#else
{ &kNS_SAMBANTLMAUTH_CID, false, nullptr, nsSambaNTLMAuthConstructor },
#endif
{ &kNS_HTTPNEGOTIATEAUTH_CID, false, nullptr, nsHttpNegotiateAuthConstructor },
{ &kNS_AUTHSASL_CID, false, nullptr, nsAuthSASLConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kAuthContracts[] = {
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-gss", &kNS_GSSAUTH_CID },
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss", &kNS_NEGOTIATEAUTH_CID },
#if defined( USE_SSPI )
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi", &kNS_NEGOTIATEAUTHSSPI_CID },
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-sspi", &kNS_KERBAUTHSSPI_CID },
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", &kNS_SYSNTLMAUTH_CID },
#else
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", &kNS_SAMBANTLMAUTH_CID },
#endif
{ NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "negotiate", &kNS_HTTPNEGOTIATEAUTH_CID },
{ NS_AUTH_MODULE_CONTRACTID_PREFIX "sasl-gssapi", &kNS_AUTHSASL_CID },
{ nullptr }
};
//-----------------------------------------------------------------------------
mozilla::LazyLogModule gNegotiateLog("negotiateauth");
// setup nspr logging ...
static nsresult
InitNegotiateAuth()
{
return NS_OK;
}
static void
DestroyNegotiateAuth()
{
nsAuthGSSAPI::Shutdown();
}
static const mozilla::Module kAuthModule = {
mozilla::Module::kVersion,
kAuthCIDs,
kAuthContracts,
nullptr,
nullptr,
InitNegotiateAuth,
DestroyNegotiateAuth
};
NSMODULE_DEFN(nsAuthModule) = &kAuthModule;

View file

@ -0,0 +1,603 @@
/* vim:set ts=4 sw=4 sts=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// GSSAPI Authentication Support Module
//
// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
// (formerly draft-brezak-spnego-http-04.txt)
//
// Also described here:
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
//
//
#include "mozilla/ArrayUtils.h"
#include "prlink.h"
#include "nsCOMPtr.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIServiceManager.h"
#include "nsNativeCharsetUtils.h"
#include "mozilla/Telemetry.h"
#include "nsAuthGSSAPI.h"
#ifdef XP_MACOSX
#include <Kerberos/Kerberos.h>
#endif
#ifdef XP_MACOSX
typedef KLStatus (*KLCacheHasValidTickets_type)(
KLPrincipal,
KLKerberosVersion,
KLBoolean *,
KLPrincipal *,
char **);
#endif
#if defined(HAVE_RES_NINIT)
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#endif
using namespace mozilla;
//-----------------------------------------------------------------------------
// We define GSS_C_NT_HOSTBASED_SERVICE explicitly since it may be referenced
// by by a different name depending on the implementation of gss but always
// has the same value
static gss_OID_desc gss_c_nt_hostbased_service =
{ 10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04" };
static const char kNegotiateAuthGssLib[] =
"network.negotiate-auth.gsslib";
static const char kNegotiateAuthNativeImp[] =
"network.negotiate-auth.using-native-gsslib";
static struct GSSFunction {
const char *str;
PRFuncPtr func;
} gssFuncs[] = {
{ "gss_display_status", nullptr },
{ "gss_init_sec_context", nullptr },
{ "gss_indicate_mechs", nullptr },
{ "gss_release_oid_set", nullptr },
{ "gss_delete_sec_context", nullptr },
{ "gss_import_name", nullptr },
{ "gss_release_buffer", nullptr },
{ "gss_release_name", nullptr },
{ "gss_wrap", nullptr },
{ "gss_unwrap", nullptr }
};
static bool gssNativeImp = true;
static PRLibrary* gssLibrary = nullptr;
#define gss_display_status_ptr ((gss_display_status_type)*gssFuncs[0].func)
#define gss_init_sec_context_ptr ((gss_init_sec_context_type)*gssFuncs[1].func)
#define gss_indicate_mechs_ptr ((gss_indicate_mechs_type)*gssFuncs[2].func)
#define gss_release_oid_set_ptr ((gss_release_oid_set_type)*gssFuncs[3].func)
#define gss_delete_sec_context_ptr ((gss_delete_sec_context_type)*gssFuncs[4].func)
#define gss_import_name_ptr ((gss_import_name_type)*gssFuncs[5].func)
#define gss_release_buffer_ptr ((gss_release_buffer_type)*gssFuncs[6].func)
#define gss_release_name_ptr ((gss_release_name_type)*gssFuncs[7].func)
#define gss_wrap_ptr ((gss_wrap_type)*gssFuncs[8].func)
#define gss_unwrap_ptr ((gss_unwrap_type)*gssFuncs[9].func)
#ifdef XP_MACOSX
static PRFuncPtr KLCacheHasValidTicketsPtr;
#define KLCacheHasValidTickets_ptr \
((KLCacheHasValidTickets_type)*KLCacheHasValidTicketsPtr)
#endif
static nsresult
gssInit()
{
nsXPIDLCString libPath;
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
prefs->GetCharPref(kNegotiateAuthGssLib, getter_Copies(libPath));
prefs->GetBoolPref(kNegotiateAuthNativeImp, &gssNativeImp);
}
PRLibrary *lib = nullptr;
if (!libPath.IsEmpty()) {
LOG(("Attempting to load user specified library [%s]\n", libPath.get()));
gssNativeImp = false;
lib = PR_LoadLibrary(libPath.get());
}
else {
#ifdef XP_WIN
char *libName = PR_GetLibraryName(nullptr, "gssapi32");
if (libName) {
lib = PR_LoadLibrary("gssapi32");
PR_FreeLibraryName(libName);
}
#elif defined(__OpenBSD__)
/* OpenBSD doesn't register inter-library dependencies in basesystem
* libs therefor we need to load all the libraries gssapi depends on,
* in the correct order and with LD_GLOBAL for GSSAPI auth to work
* fine.
*/
const char *const verLibNames[] = {
"libasn1.so",
"libcrypto.so",
"libroken.so",
"libheimbase.so",
"libcom_err.so",
"libkrb5.so",
"libgssapi.so"
};
PRLibSpec libSpec;
for (size_t i = 0; i < ArrayLength(verLibNames); ++i) {
libSpec.type = PR_LibSpec_Pathname;
libSpec.value.pathname = verLibNames[i];
lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_GLOBAL);
}
#else
const char *const libNames[] = {
"gss",
"gssapi_krb5",
"gssapi"
};
const char *const verLibNames[] = {
"libgssapi_krb5.so.2", /* MIT - FC, Suse10, Debian */
"libgssapi.so.4", /* Heimdal - Suse10, MDK */
"libgssapi.so.1" /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/
};
for (size_t i = 0; i < ArrayLength(verLibNames) && !lib; ++i) {
lib = PR_LoadLibrary(verLibNames[i]);
/* The CITI libgssapi library calls exit() during
* initialization if it's not correctly configured. Try to
* ensure that we never use this library for our GSSAPI
* support, as its just a wrapper library, anyway.
* See Bugzilla #325433
*/
if (lib &&
PR_FindFunctionSymbol(lib,
"internal_krb5_gss_initialize") &&
PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
PR_UnloadLibrary(lib);
lib = nullptr;
}
}
for (size_t i = 0; i < ArrayLength(libNames) && !lib; ++i) {
char *libName = PR_GetLibraryName(nullptr, libNames[i]);
if (libName) {
lib = PR_LoadLibrary(libName);
PR_FreeLibraryName(libName);
if (lib &&
PR_FindFunctionSymbol(lib,
"internal_krb5_gss_initialize") &&
PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
PR_UnloadLibrary(lib);
lib = nullptr;
}
}
}
#endif
}
if (!lib) {
LOG(("Fail to load gssapi library\n"));
return NS_ERROR_FAILURE;
}
LOG(("Attempting to load gss functions\n"));
for (size_t i = 0; i < ArrayLength(gssFuncs); ++i) {
gssFuncs[i].func = PR_FindFunctionSymbol(lib, gssFuncs[i].str);
if (!gssFuncs[i].func) {
LOG(("Fail to load %s function from gssapi library\n", gssFuncs[i].str));
PR_UnloadLibrary(lib);
return NS_ERROR_FAILURE;
}
}
#ifdef XP_MACOSX
if (gssNativeImp &&
!(KLCacheHasValidTicketsPtr =
PR_FindFunctionSymbol(lib, "KLCacheHasValidTickets"))) {
LOG(("Fail to load KLCacheHasValidTickets function from gssapi library\n"));
PR_UnloadLibrary(lib);
return NS_ERROR_FAILURE;
}
#endif
gssLibrary = lib;
return NS_OK;
}
// Generate proper GSSAPI error messages from the major and
// minor status codes.
void
LogGssError(OM_uint32 maj_stat, OM_uint32 min_stat, const char *prefix)
{
if (!MOZ_LOG_TEST(gNegotiateLog, LogLevel::Debug)) {
return;
}
OM_uint32 new_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status1_string;
gss_buffer_desc status2_string;
OM_uint32 ret;
nsAutoCString errorStr;
errorStr.Assign(prefix);
if (!gssLibrary)
return;
errorStr += ": ";
do {
ret = gss_display_status_ptr(&new_stat,
maj_stat,
GSS_C_GSS_CODE,
GSS_C_NULL_OID,
&msg_ctx,
&status1_string);
errorStr.Append((const char *) status1_string.value, status1_string.length);
gss_release_buffer_ptr(&new_stat, &status1_string);
errorStr += '\n';
ret = gss_display_status_ptr(&new_stat,
min_stat,
GSS_C_MECH_CODE,
GSS_C_NULL_OID,
&msg_ctx,
&status2_string);
errorStr.Append((const char *) status2_string.value, status2_string.length);
errorStr += '\n';
} while (!GSS_ERROR(ret) && msg_ctx != 0);
LOG(("%s\n", errorStr.get()));
}
//-----------------------------------------------------------------------------
nsAuthGSSAPI::nsAuthGSSAPI(pType package)
: mServiceFlags(REQ_DEFAULT)
{
OM_uint32 minstat;
OM_uint32 majstat;
gss_OID_set mech_set;
gss_OID item;
unsigned int i;
static gss_OID_desc gss_krb5_mech_oid_desc =
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
static gss_OID_desc gss_spnego_mech_oid_desc =
{ 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
LOG(("entering nsAuthGSSAPI::nsAuthGSSAPI()\n"));
mComplete = false;
if (!gssLibrary && NS_FAILED(gssInit()))
return;
mCtx = GSS_C_NO_CONTEXT;
mMechOID = &gss_krb5_mech_oid_desc;
// if the type is kerberos we accept it as default
// and exit
if (package == PACKAGE_TYPE_KERBEROS)
return;
// Now, look at the list of supported mechanisms,
// if SPNEGO is found, then use it.
// Otherwise, set the desired mechanism to
// GSS_C_NO_OID and let the system try to use
// the default mechanism.
//
// Using Kerberos directly (instead of negotiating
// with SPNEGO) may work in some cases depending
// on how smart the server side is.
majstat = gss_indicate_mechs_ptr(&minstat, &mech_set);
if (GSS_ERROR(majstat))
return;
if (mech_set) {
for (i=0; i<mech_set->count; i++) {
item = &mech_set->elements[i];
if (item->length == gss_spnego_mech_oid_desc.length &&
!memcmp(item->elements, gss_spnego_mech_oid_desc.elements,
item->length)) {
// ok, we found it
mMechOID = &gss_spnego_mech_oid_desc;
break;
}
}
gss_release_oid_set_ptr(&minstat, &mech_set);
}
}
void
nsAuthGSSAPI::Reset()
{
if (gssLibrary && mCtx != GSS_C_NO_CONTEXT) {
OM_uint32 minor_status;
gss_delete_sec_context_ptr(&minor_status, &mCtx, GSS_C_NO_BUFFER);
}
mCtx = GSS_C_NO_CONTEXT;
mComplete = false;
}
/* static */ void
nsAuthGSSAPI::Shutdown()
{
if (gssLibrary) {
PR_UnloadLibrary(gssLibrary);
gssLibrary = nullptr;
}
}
/* Limitations apply to this class's thread safety. See the header file */
NS_IMPL_ISUPPORTS(nsAuthGSSAPI, nsIAuthModule)
NS_IMETHODIMP
nsAuthGSSAPI::Init(const char *serviceName,
uint32_t serviceFlags,
const char16_t *domain,
const char16_t *username,
const char16_t *password)
{
// we don't expect to be passed any user credentials
NS_ASSERTION(!domain && !username && !password, "unexpected credentials");
// it's critial that the caller supply a service name to be used
NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
LOG(("entering nsAuthGSSAPI::Init()\n"));
if (!gssLibrary)
return NS_ERROR_NOT_INITIALIZED;
mServiceName = serviceName;
mServiceFlags = serviceFlags;
static bool sTelemetrySent = false;
if (!sTelemetrySent) {
mozilla::Telemetry::Accumulate(
mozilla::Telemetry::NTLM_MODULE_USED_2,
serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
? NTLM_MODULE_KERBEROS_PROXY
: NTLM_MODULE_KERBEROS_DIRECT);
sTelemetrySent = true;
}
return NS_OK;
}
NS_IMETHODIMP
nsAuthGSSAPI::GetNextToken(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
OM_uint32 major_status, minor_status;
OM_uint32 req_flags = 0;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_buffer_t in_token_ptr = GSS_C_NO_BUFFER;
gss_name_t server;
nsAutoCString userbuf;
nsresult rv;
LOG(("entering nsAuthGSSAPI::GetNextToken()\n"));
if (!gssLibrary)
return NS_ERROR_NOT_INITIALIZED;
// If they've called us again after we're complete, reset to start afresh.
if (mComplete)
Reset();
if (mServiceFlags & REQ_DELEGATE)
req_flags |= GSS_C_DELEG_FLAG;
if (mServiceFlags & REQ_MUTUAL_AUTH)
req_flags |= GSS_C_MUTUAL_FLAG;
input_token.value = (void *)mServiceName.get();
input_token.length = mServiceName.Length() + 1;
#if defined(HAVE_RES_NINIT)
res_ninit(&_res);
#endif
major_status = gss_import_name_ptr(&minor_status,
&input_token,
&gss_c_nt_hostbased_service,
&server);
input_token.value = nullptr;
input_token.length = 0;
if (GSS_ERROR(major_status)) {
LogGssError(major_status, minor_status, "gss_import_name() failed");
return NS_ERROR_FAILURE;
}
if (inToken) {
input_token.length = inTokenLen;
input_token.value = (void *) inToken;
in_token_ptr = &input_token;
}
else if (mCtx != GSS_C_NO_CONTEXT) {
// If there is no input token, then we are starting a new
// authentication sequence. If we have already initialized our
// security context, then we're in trouble because it means that the
// first sequence failed. We need to bail or else we might end up in
// an infinite loop.
LOG(("Cannot restart authentication sequence!"));
return NS_ERROR_UNEXPECTED;
}
#if defined(XP_MACOSX)
// Suppress Kerberos prompts to get credentials. See bug 240643.
// We can only use Mac OS X specific kerb functions if we are using
// the native lib
KLBoolean found;
bool doingMailTask = mServiceName.Find("imap@") ||
mServiceName.Find("pop@") ||
mServiceName.Find("smtp@") ||
mServiceName.Find("ldap@");
if (!doingMailTask && (gssNativeImp &&
(KLCacheHasValidTickets_ptr(nullptr, kerberosVersion_V5, &found, nullptr, nullptr) != klNoErr || !found)))
{
major_status = GSS_S_FAILURE;
minor_status = 0;
}
else
#endif /* XP_MACOSX */
major_status = gss_init_sec_context_ptr(&minor_status,
GSS_C_NO_CREDENTIAL,
&mCtx,
server,
mMechOID,
req_flags,
GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
in_token_ptr,
nullptr,
&output_token,
nullptr,
nullptr);
if (GSS_ERROR(major_status)) {
LogGssError(major_status, minor_status, "gss_init_sec_context() failed");
Reset();
rv = NS_ERROR_FAILURE;
goto end;
}
if (major_status == GSS_S_COMPLETE) {
// Mark ourselves as being complete, so that if we're called again
// we know to start afresh.
mComplete = true;
}
else if (major_status == GSS_S_CONTINUE_NEEDED) {
//
// The important thing is that we do NOT reset the
// context here because it will be needed on the
// next call.
//
}
*outTokenLen = output_token.length;
if (output_token.length != 0)
*outToken = nsMemory::Clone(output_token.value, output_token.length);
else
*outToken = nullptr;
gss_release_buffer_ptr(&minor_status, &output_token);
if (major_status == GSS_S_COMPLETE)
rv = NS_SUCCESS_AUTH_FINISHED;
else
rv = NS_OK;
end:
gss_release_name_ptr(&minor_status, &server);
LOG((" leaving nsAuthGSSAPI::GetNextToken [rv=%x]", rv));
return rv;
}
NS_IMETHODIMP
nsAuthGSSAPI::Unwrap(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
OM_uint32 major_status, minor_status;
gss_buffer_desc input_token;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
input_token.value = (void *) inToken;
input_token.length = inTokenLen;
major_status = gss_unwrap_ptr(&minor_status,
mCtx,
&input_token,
&output_token,
nullptr,
nullptr);
if (GSS_ERROR(major_status)) {
LogGssError(major_status, minor_status, "gss_unwrap() failed");
Reset();
gss_release_buffer_ptr(&minor_status, &output_token);
return NS_ERROR_FAILURE;
}
*outTokenLen = output_token.length;
if (output_token.length)
*outToken = nsMemory::Clone(output_token.value, output_token.length);
else
*outToken = nullptr;
gss_release_buffer_ptr(&minor_status, &output_token);
return NS_OK;
}
NS_IMETHODIMP
nsAuthGSSAPI::Wrap(const void *inToken,
uint32_t inTokenLen,
bool confidential,
void **outToken,
uint32_t *outTokenLen)
{
OM_uint32 major_status, minor_status;
gss_buffer_desc input_token;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
input_token.value = (void *) inToken;
input_token.length = inTokenLen;
major_status = gss_wrap_ptr(&minor_status,
mCtx,
confidential,
GSS_C_QOP_DEFAULT,
&input_token,
nullptr,
&output_token);
if (GSS_ERROR(major_status)) {
LogGssError(major_status, minor_status, "gss_wrap() failed");
Reset();
gss_release_buffer_ptr(&minor_status, &output_token);
return NS_ERROR_FAILURE;
}
*outTokenLen = output_token.length;
/* it is not possible for output_token.length to be zero */
*outToken = nsMemory::Clone(output_token.value, output_token.length);
gss_release_buffer_ptr(&minor_status, &output_token);
return NS_OK;
}

View file

@ -0,0 +1,63 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAuthGSSAPI_h__
#define nsAuthGSSAPI_h__
#include "nsAuth.h"
#include "nsIAuthModule.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
#define GSS_USE_FUNCTION_POINTERS 1
#include "gssapi.h"
// The nsAuthGSSAPI class provides responses for the GSS-API Negotiate method
// as specified by Microsoft in draft-brezak-spnego-http-04.txt
/* Some remarks on thread safety ...
*
* The thread safety of this class depends largely upon the thread safety of
* the underlying GSSAPI and Kerberos libraries. This code just loads the
* system GSSAPI library, and whilst it avoids loading known bad libraries,
* it cannot determine the thread safety of the the code it loads.
*
* When used with a non-threadsafe library, it is not safe to simultaneously
* use multiple instantiations of this class.
*
* When used with a threadsafe Kerberos library, multiple instantiations of
* this class may happily co-exist. Methods may be sequentially called from
* multiple threads. The nature of the GSSAPI protocol is such that a correct
* implementation will never call methods in parallel, as the results of the
* last call are required as input to the next.
*/
class nsAuthGSSAPI final : public nsIAuthModule
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIAUTHMODULE
explicit nsAuthGSSAPI(pType package);
static void Shutdown();
private:
~nsAuthGSSAPI() { Reset(); }
void Reset();
gss_OID GetOID() { return mMechOID; }
private:
gss_ctx_id_t mCtx;
gss_OID mMechOID;
nsCString mServiceName;
uint32_t mServiceFlags;
nsString mUsername;
bool mComplete;
};
#endif /* nsAuthGSSAPI_h__ */

View file

@ -0,0 +1,151 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsComponentManagerUtils.h"
#include "nsNativeCharsetUtils.h"
#include "nsIServiceManager.h"
#include "nsIPrefService.h"
#include "nsAuthSASL.h"
static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
nsAuthSASL::nsAuthSASL()
{
mSASLReady = false;
}
void nsAuthSASL::Reset()
{
mSASLReady = false;
}
/* Limitations apply to this class's thread safety. See the header file */
NS_IMPL_ISUPPORTS(nsAuthSASL, nsIAuthModule)
NS_IMETHODIMP
nsAuthSASL::Init(const char *serviceName,
uint32_t serviceFlags,
const char16_t *domain,
const char16_t *username,
const char16_t *password)
{
nsresult rv;
NS_ASSERTION(username, "SASL requires a username");
NS_ASSERTION(!domain && !password, "unexpected credentials");
mUsername = username;
// If we're doing SASL, we should do mutual auth
serviceFlags |= REQ_MUTUAL_AUTH;
// Find out whether we should be trying SSPI or not
const char *contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-gss";
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
bool val;
rv = prefs->GetBoolPref(kNegotiateAuthSSPI, &val);
if (NS_SUCCEEDED(rv) && val)
contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-sspi";
}
mInnerModule = do_CreateInstance(contractID, &rv);
// if we can't create the GSSAPI module, then bail
NS_ENSURE_SUCCESS(rv, rv);
mInnerModule->Init(serviceName, serviceFlags, nullptr, nullptr, nullptr);
return NS_OK;
}
NS_IMETHODIMP
nsAuthSASL::GetNextToken(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
nsresult rv;
void *unwrappedToken;
char *message;
uint32_t unwrappedTokenLen, messageLen;
nsAutoCString userbuf;
if (!mInnerModule)
return NS_ERROR_NOT_INITIALIZED;
if (mSASLReady) {
// If the server COMPLETEs with an empty token, Cyrus sends us that token.
// I don't think this is correct, but we need to handle that behaviour.
// Cyrus ignores the contents of our reply token.
if (inTokenLen == 0) {
*outToken = nullptr;
*outTokenLen = 0;
return NS_OK;
}
// We've completed the GSSAPI portion of the handshake, and are
// now ready to do the SASL security layer and authzid negotiation
// Input packet from the server needs to be unwrapped.
rv = mInnerModule->Unwrap(inToken, inTokenLen, &unwrappedToken,
&unwrappedTokenLen);
if (NS_FAILED(rv)) {
Reset();
return rv;
}
// If we were doing security layers then we'd care what the
// server had sent us. We're not, so all we had to do was make
// sure that the signature was correct with the above unwrap()
free(unwrappedToken);
NS_CopyUnicodeToNative(mUsername, userbuf);
messageLen = userbuf.Length() + 4 + 1;
message = (char *)moz_xmalloc(messageLen);
if (!message) {
Reset();
return NS_ERROR_OUT_OF_MEMORY;
}
message[0] = 0x01; // No security layer
message[1] = 0x00;
message[2] = 0x00;
message[3] = 0x00; // Maxbuf must be zero if we've got no sec layer
strcpy(message+4, userbuf.get());
// Userbuf should not be nullptr terminated, so trim the trailing nullptr
// when wrapping the message
rv = mInnerModule->Wrap((void *) message, messageLen-1, false,
outToken, outTokenLen);
free(message);
Reset(); // All done
return NS_SUCCEEDED(rv) ? NS_SUCCESS_AUTH_FINISHED : rv;
}
rv = mInnerModule->GetNextToken(inToken, inTokenLen, outToken,
outTokenLen);
if (rv == NS_SUCCESS_AUTH_FINISHED) {
mSASLReady = true;
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
nsAuthSASL::Unwrap(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsAuthSASL::Wrap(const void *inToken,
uint32_t inTokenLen,
bool confidential,
void **outToken,
uint32_t *outTokenLen)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,38 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAuthSASL_h__
#define nsAuthSASL_h__
#include "nsIAuthModule.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
/* This class is implemented using the nsAuthGSSAPI class, and the same
* thread safety constraints which are documented in nsAuthGSSAPI.h
* apply to this class
*/
class nsAuthSASL final : public nsIAuthModule
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIAUTHMODULE
nsAuthSASL();
private:
~nsAuthSASL() { Reset(); }
void Reset();
nsCOMPtr<nsIAuthModule> mInnerModule;
nsString mUsername;
bool mSASLReady;
};
#endif /* nsAuthSASL_h__ */

View file

@ -0,0 +1,666 @@
/* vim:set ts=4 sw=4 sts=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// Negotiate Authentication Support Module
//
// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
// (formerly draft-brezak-spnego-http-04.txt)
//
// Also described here:
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
//
#include "nsAuthSSPI.h"
#include "nsIServiceManager.h"
#include "nsIDNSService.h"
#include "nsIDNSRecord.h"
#include "nsNetCID.h"
#include "nsCOMPtr.h"
#include "nsICryptoHash.h"
#include "mozilla/Telemetry.h"
#include <windows.h>
#define SEC_SUCCESS(Status) ((Status) >= 0)
#ifndef KERB_WRAP_NO_ENCRYPT
#define KERB_WRAP_NO_ENCRYPT 0x80000001
#endif
#ifndef SECBUFFER_PADDING
#define SECBUFFER_PADDING 9
#endif
#ifndef SECBUFFER_STREAM
#define SECBUFFER_STREAM 10
#endif
//-----------------------------------------------------------------------------
static const wchar_t *const pTypeName [] = {
L"Kerberos",
L"Negotiate",
L"NTLM"
};
#ifdef DEBUG
#define CASE_(_x) case _x: return # _x;
static const char *MapErrorCode(int rc)
{
switch (rc) {
CASE_(SEC_E_OK)
CASE_(SEC_I_CONTINUE_NEEDED)
CASE_(SEC_I_COMPLETE_NEEDED)
CASE_(SEC_I_COMPLETE_AND_CONTINUE)
CASE_(SEC_E_INCOMPLETE_MESSAGE)
CASE_(SEC_I_INCOMPLETE_CREDENTIALS)
CASE_(SEC_E_INVALID_HANDLE)
CASE_(SEC_E_TARGET_UNKNOWN)
CASE_(SEC_E_LOGON_DENIED)
CASE_(SEC_E_INTERNAL_ERROR)
CASE_(SEC_E_NO_CREDENTIALS)
CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY)
CASE_(SEC_E_INSUFFICIENT_MEMORY)
CASE_(SEC_E_INVALID_TOKEN)
}
return "<unknown>";
}
#else
#define MapErrorCode(_rc) ""
#endif
//-----------------------------------------------------------------------------
static PSecurityFunctionTableW sspi;
static nsresult
InitSSPI()
{
LOG((" InitSSPI\n"));
sspi = InitSecurityInterfaceW();
if (!sspi) {
LOG(("InitSecurityInterfaceW failed"));
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
static nsresult
MakeSN(const char *principal, nsCString &result)
{
nsresult rv;
nsAutoCString buf(principal);
// The service name looks like "protocol@hostname", we need to map
// this to a value that SSPI expects. To be consistent with IE, we
// need to map '@' to '/' and canonicalize the hostname.
int32_t index = buf.FindChar('@');
if (index == kNotFound)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
// This could be expensive if our DNS cache cannot satisfy the request.
// However, we should have at least hit the OS resolver once prior to
// reaching this code, so provided the OS resolver has this information
// cached, we should not have to worry about blocking on this function call
// for very long. NOTE: because we ask for the canonical hostname, we
// might end up requiring extra network activity in cases where the OS
// resolver might not have enough information to satisfy the request from
// its cache. This is not an issue in versions of Windows up to WinXP.
nsCOMPtr<nsIDNSRecord> record;
rv = dns->Resolve(Substring(buf, index + 1),
nsIDNSService::RESOLVE_CANONICAL_NAME,
getter_AddRefs(record));
if (NS_FAILED(rv))
return rv;
nsAutoCString cname;
rv = record->GetCanonicalName(cname);
if (NS_SUCCEEDED(rv)) {
result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname;
LOG(("Using SPN of [%s]\n", result.get()));
}
return rv;
}
//-----------------------------------------------------------------------------
nsAuthSSPI::nsAuthSSPI(pType package)
: mServiceFlags(REQ_DEFAULT)
, mMaxTokenLen(0)
, mPackage(package)
, mCertDERData(nullptr)
, mCertDERLength(0)
{
memset(&mCred, 0, sizeof(mCred));
memset(&mCtxt, 0, sizeof(mCtxt));
}
nsAuthSSPI::~nsAuthSSPI()
{
Reset();
if (mCred.dwLower || mCred.dwUpper) {
#ifdef __MINGW32__
(sspi->FreeCredentialsHandle)(&mCred);
#else
(sspi->FreeCredentialHandle)(&mCred);
#endif
memset(&mCred, 0, sizeof(mCred));
}
}
void
nsAuthSSPI::Reset()
{
mIsFirst = true;
if (mCertDERData){
free(mCertDERData);
mCertDERData = nullptr;
mCertDERLength = 0;
}
if (mCtxt.dwLower || mCtxt.dwUpper) {
(sspi->DeleteSecurityContext)(&mCtxt);
memset(&mCtxt, 0, sizeof(mCtxt));
}
}
NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule)
NS_IMETHODIMP
nsAuthSSPI::Init(const char *serviceName,
uint32_t serviceFlags,
const char16_t *domain,
const char16_t *username,
const char16_t *password)
{
LOG((" nsAuthSSPI::Init\n"));
mIsFirst = true;
mCertDERLength = 0;
mCertDERData = nullptr;
// The caller must supply a service name to be used. (For why we now require
// a service name for NTLM, see bug 487872.)
NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
nsresult rv;
// XXX lazy initialization like this assumes that we are single threaded
if (!sspi) {
rv = InitSSPI();
if (NS_FAILED(rv))
return rv;
}
SEC_WCHAR *package;
package = (SEC_WCHAR *) pTypeName[(int)mPackage];
if (mPackage == PACKAGE_TYPE_NTLM) {
// (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups.
// The incoming serviceName is in the format: "protocol@hostname", SSPI expects
// "<service class>/<hostname>", so swap the '@' for a '/'.
mServiceName.Assign(serviceName);
int32_t index = mServiceName.FindChar('@');
if (index == kNotFound)
return NS_ERROR_UNEXPECTED;
mServiceName.Replace(index, 1, '/');
}
else {
// Kerberos requires the canonical host, MakeSN takes care of this through a
// DNS lookup.
rv = MakeSN(serviceName, mServiceName);
if (NS_FAILED(rv))
return rv;
}
mServiceFlags = serviceFlags;
SECURITY_STATUS rc;
PSecPkgInfoW pinfo;
rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo);
if (rc != SEC_E_OK) {
LOG(("%s package not found\n", package));
return NS_ERROR_UNEXPECTED;
}
mMaxTokenLen = pinfo->cbMaxToken;
(sspi->FreeContextBuffer)(pinfo);
MS_TimeStamp useBefore;
SEC_WINNT_AUTH_IDENTITY_W ai;
SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr;
// domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived
// returns false for identityInvalid. Use default credentials in this case by passing
// null for pai.
if (username && password) {
// Keep a copy of these strings for the duration
mUsername.Assign(username);
mPassword.Assign(password);
mDomain.Assign(domain);
ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting());
ai.DomainLength = mDomain.Length();
ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting());
ai.UserLength = mUsername.Length();
ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting());
ai.PasswordLength = mPassword.Length();
ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
pai = &ai;
}
rc = (sspi->AcquireCredentialsHandleW)(nullptr,
package,
SECPKG_CRED_OUTBOUND,
nullptr,
pai,
nullptr,
nullptr,
&mCred,
&useBefore);
if (rc != SEC_E_OK)
return NS_ERROR_UNEXPECTED;
static bool sTelemetrySent = false;
if (!sTelemetrySent) {
mozilla::Telemetry::Accumulate(
mozilla::Telemetry::NTLM_MODULE_USED_2,
serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
? NTLM_MODULE_WIN_API_PROXY
: NTLM_MODULE_WIN_API_DIRECT);
sTelemetrySent = true;
}
LOG(("AcquireCredentialsHandle() succeeded.\n"));
return NS_OK;
}
// The arguments inToken and inTokenLen are used to pass in the server
// certificate (when available) in the first call of the function. The
// second time these arguments hold an input token.
NS_IMETHODIMP
nsAuthSSPI::GetNextToken(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
// String for end-point bindings.
const char end_point[] = "tls-server-end-point:";
const int end_point_length = sizeof(end_point) - 1;
const int hash_size = 32; // Size of a SHA256 hash.
const int cbt_size = hash_size + end_point_length;
SECURITY_STATUS rc;
MS_TimeStamp ignored;
DWORD ctxAttr, ctxReq = 0;
CtxtHandle *ctxIn;
SecBufferDesc ibd, obd;
// Optional second input buffer for the CBT (Channel Binding Token)
SecBuffer ib[2], ob;
// Pointer to the block of memory that stores the CBT
char* sspi_cbt = nullptr;
SEC_CHANNEL_BINDINGS pendpoint_binding;
LOG(("entering nsAuthSSPI::GetNextToken()\n"));
if (!mCred.dwLower && !mCred.dwUpper) {
LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
return NS_ERROR_NOT_INITIALIZED;
}
if (mServiceFlags & REQ_DELEGATE)
ctxReq |= ISC_REQ_DELEGATE;
if (mServiceFlags & REQ_MUTUAL_AUTH)
ctxReq |= ISC_REQ_MUTUAL_AUTH;
if (inToken) {
if (mIsFirst) {
// First time if it comes with a token,
// the token represents the server certificate.
mIsFirst = false;
mCertDERLength = inTokenLen;
mCertDERData = moz_xmalloc(inTokenLen);
if (!mCertDERData)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mCertDERData, inToken, inTokenLen);
// We are starting a new authentication sequence.
// If we have already initialized our
// security context, then we're in trouble because it means that the
// first sequence failed. We need to bail or else we might end up in
// an infinite loop.
if (mCtxt.dwLower || mCtxt.dwUpper) {
LOG(("Cannot restart authentication sequence!"));
return NS_ERROR_UNEXPECTED;
}
ctxIn = nullptr;
// The certificate needs to be erased before being passed
// to InitializeSecurityContextW().
inToken = nullptr;
inTokenLen = 0;
} else {
ibd.ulVersion = SECBUFFER_VERSION;
ibd.cBuffers = 0;
ibd.pBuffers = ib;
// If we have stored a certificate, the Channel Binding Token
// needs to be generated and sent in the first input buffer.
if (mCertDERLength > 0) {
// First we create a proper Endpoint Binding structure.
pendpoint_binding.dwInitiatorAddrType = 0;
pendpoint_binding.cbInitiatorLength = 0;
pendpoint_binding.dwInitiatorOffset = 0;
pendpoint_binding.dwAcceptorAddrType = 0;
pendpoint_binding.cbAcceptorLength = 0;
pendpoint_binding.dwAcceptorOffset = 0;
pendpoint_binding.cbApplicationDataLength = cbt_size;
pendpoint_binding.dwApplicationDataOffset =
sizeof(SEC_CHANNEL_BINDINGS);
// Then add it to the array of sec buffers accordingly.
ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
ib[ibd.cBuffers].cbBuffer =
pendpoint_binding.cbApplicationDataLength
+ pendpoint_binding.dwApplicationDataOffset;
sspi_cbt = (char *) moz_xmalloc(ib[ibd.cBuffers].cbBuffer);
if (!sspi_cbt){
return NS_ERROR_OUT_OF_MEMORY;
}
// Helper to write in the memory block that stores the CBT
char* sspi_cbt_ptr = sspi_cbt;
ib[ibd.cBuffers].pvBuffer = sspi_cbt;
ibd.cBuffers++;
memcpy(sspi_cbt_ptr, &pendpoint_binding,
pendpoint_binding.dwApplicationDataOffset);
sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
memcpy(sspi_cbt_ptr, end_point, end_point_length);
sspi_cbt_ptr += end_point_length;
// Start hashing. We are always doing SHA256, but depending
// on the certificate, a different alogirthm might be needed.
nsAutoCString hashString;
nsresult rv;
nsCOMPtr<nsICryptoHash> crypto;
crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
rv = crypto->Init(nsICryptoHash::SHA256);
if (NS_SUCCEEDED(rv))
rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
if (NS_SUCCEEDED(rv))
rv = crypto->Finish(false, hashString);
if (NS_FAILED(rv)) {
free(mCertDERData);
mCertDERData = nullptr;
mCertDERLength = 0;
free(sspi_cbt);
return rv;
}
// Once the hash has been computed, we store it in memory right
// after the Endpoint structure and the "tls-server-end-point:"
// char array.
memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
// Free memory used to store the server certificate
free(mCertDERData);
mCertDERData = nullptr;
mCertDERLength = 0;
} // End of CBT computation.
// We always need this SECBUFFER.
ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
ib[ibd.cBuffers].cbBuffer = inTokenLen;
ib[ibd.cBuffers].pvBuffer = (void *) inToken;
ibd.cBuffers++;
ctxIn = &mCtxt;
}
} else { // First time and without a token (no server certificate)
// We are starting a new authentication sequence. If we have already
// initialized our security context, then we're in trouble because it
// means that the first sequence failed. We need to bail or else we
// might end up in an infinite loop.
if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
LOG(("Cannot restart authentication sequence!"));
return NS_ERROR_UNEXPECTED;
}
ctxIn = nullptr;
mIsFirst = false;
}
obd.ulVersion = SECBUFFER_VERSION;
obd.cBuffers = 1;
obd.pBuffers = &ob;
ob.BufferType = SECBUFFER_TOKEN;
ob.cbBuffer = mMaxTokenLen;
ob.pvBuffer = moz_xmalloc(ob.cbBuffer);
if (!ob.pvBuffer){
if (sspi_cbt)
free(sspi_cbt);
return NS_ERROR_OUT_OF_MEMORY;
}
memset(ob.pvBuffer, 0, ob.cbBuffer);
NS_ConvertUTF8toUTF16 wSN(mServiceName);
SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
rc = (sspi->InitializeSecurityContextW)(&mCred,
ctxIn,
sn,
ctxReq,
0,
SECURITY_NATIVE_DREP,
inToken ? &ibd : nullptr,
0,
&mCtxt,
&obd,
&ctxAttr,
&ignored);
if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
if (rc == SEC_E_OK)
LOG(("InitializeSecurityContext: succeeded.\n"));
else
LOG(("InitializeSecurityContext: continue.\n"));
if (sspi_cbt)
free(sspi_cbt);
if (!ob.cbBuffer) {
free(ob.pvBuffer);
ob.pvBuffer = nullptr;
}
*outToken = ob.pvBuffer;
*outTokenLen = ob.cbBuffer;
if (rc == SEC_E_OK)
return NS_SUCCESS_AUTH_FINISHED;
return NS_OK;
}
LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc)));
Reset();
free(ob.pvBuffer);
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsAuthSSPI::Unwrap(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
SECURITY_STATUS rc;
SecBufferDesc ibd;
SecBuffer ib[2];
ibd.cBuffers = 2;
ibd.pBuffers = ib;
ibd.ulVersion = SECBUFFER_VERSION;
// SSPI Buf
ib[0].BufferType = SECBUFFER_STREAM;
ib[0].cbBuffer = inTokenLen;
ib[0].pvBuffer = moz_xmalloc(ib[0].cbBuffer);
if (!ib[0].pvBuffer)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(ib[0].pvBuffer, inToken, inTokenLen);
// app data
ib[1].BufferType = SECBUFFER_DATA;
ib[1].cbBuffer = 0;
ib[1].pvBuffer = nullptr;
rc = (sspi->DecryptMessage)(
&mCtxt,
&ibd,
0, // no sequence numbers
nullptr
);
if (SEC_SUCCESS(rc)) {
// check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which
// case we can let the caller free it. Otherwise, we need to
// clone it, and free the original
if (ib[0].pvBuffer == ib[1].pvBuffer) {
*outToken = ib[1].pvBuffer;
}
else {
*outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer);
free(ib[0].pvBuffer);
if (!*outToken)
return NS_ERROR_OUT_OF_MEMORY;
}
*outTokenLen = ib[1].cbBuffer;
}
else
free(ib[0].pvBuffer);
if (!SEC_SUCCESS(rc))
return NS_ERROR_FAILURE;
return NS_OK;
}
// utility class used to free memory on exit
class secBuffers
{
public:
SecBuffer ib[3];
secBuffers() { memset(&ib, 0, sizeof(ib)); }
~secBuffers()
{
if (ib[0].pvBuffer)
free(ib[0].pvBuffer);
if (ib[1].pvBuffer)
free(ib[1].pvBuffer);
if (ib[2].pvBuffer)
free(ib[2].pvBuffer);
}
};
NS_IMETHODIMP
nsAuthSSPI::Wrap(const void *inToken,
uint32_t inTokenLen,
bool confidential,
void **outToken,
uint32_t *outTokenLen)
{
SECURITY_STATUS rc;
SecBufferDesc ibd;
secBuffers bufs;
SecPkgContext_Sizes sizes;
rc = (sspi->QueryContextAttributesW)(
&mCtxt,
SECPKG_ATTR_SIZES,
&sizes);
if (!SEC_SUCCESS(rc))
return NS_ERROR_FAILURE;
ibd.cBuffers = 3;
ibd.pBuffers = bufs.ib;
ibd.ulVersion = SECBUFFER_VERSION;
// SSPI
bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer;
bufs.ib[0].BufferType = SECBUFFER_TOKEN;
bufs.ib[0].pvBuffer = moz_xmalloc(sizes.cbSecurityTrailer);
if (!bufs.ib[0].pvBuffer)
return NS_ERROR_OUT_OF_MEMORY;
// APP Data
bufs.ib[1].BufferType = SECBUFFER_DATA;
bufs.ib[1].pvBuffer = moz_xmalloc(inTokenLen);
bufs.ib[1].cbBuffer = inTokenLen;
if (!bufs.ib[1].pvBuffer)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen);
// SSPI
bufs.ib[2].BufferType = SECBUFFER_PADDING;
bufs.ib[2].cbBuffer = sizes.cbBlockSize;
bufs.ib[2].pvBuffer = moz_xmalloc(bufs.ib[2].cbBuffer);
if (!bufs.ib[2].pvBuffer)
return NS_ERROR_OUT_OF_MEMORY;
rc = (sspi->EncryptMessage)(&mCtxt,
confidential ? 0 : KERB_WRAP_NO_ENCRYPT,
&ibd, 0);
if (SEC_SUCCESS(rc)) {
int len = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer;
char *p = (char *) moz_xmalloc(len);
if (!p)
return NS_ERROR_OUT_OF_MEMORY;
*outToken = (void *) p;
*outTokenLen = len;
memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer);
p += bufs.ib[0].cbBuffer;
memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer);
p += bufs.ib[1].cbBuffer;
memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer);
return NS_OK;
}
return NS_ERROR_FAILURE;
}

View file

@ -0,0 +1,59 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAuthSSPI_h__
#define nsAuthSSPI_h__
#include "nsAuth.h"
#include "nsIAuthModule.h"
#include "nsString.h"
#include <windows.h>
#define SECURITY_WIN32 1
#include <ntsecapi.h>
#include <security.h>
#include <rpc.h>
// The nsNegotiateAuth class provides responses for the GSS-API Negotiate method
// as specified by Microsoft in draft-brezak-spnego-http-04.txt
// It can also be configured to talk raw NTLM. This implementation of NTLM has
// the advantage of being able to access the user's logon credentials. This
// implementation of NTLM should only be used for single-signon. It should be
// avoided when authenticating over the internet since it may use a lower-grade
// version of password hashing depending on the version of Windows being used.
class nsAuthSSPI final : public nsIAuthModule
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTHMODULE
nsAuthSSPI(pType package = PACKAGE_TYPE_NEGOTIATE);
private:
~nsAuthSSPI();
void Reset();
typedef TimeStamp MS_TimeStamp;
private:
CredHandle mCred;
CtxtHandle mCtxt;
nsCString mServiceName;
uint32_t mServiceFlags;
uint32_t mMaxTokenLen;
pType mPackage;
nsString mDomain;
nsString mUsername;
nsString mPassword;
bool mIsFirst;
void* mCertDERData;
uint32_t mCertDERLength;
};
#endif /* nsAuthSSPI_h__ */

View file

@ -0,0 +1,296 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAuth.h"
#include "nsAuthSambaNTLM.h"
#include "prenv.h"
#include "plbase64.h"
#include "prerror.h"
#include "mozilla/Telemetry.h"
#include <stdlib.h>
nsAuthSambaNTLM::nsAuthSambaNTLM()
: mInitialMessage(nullptr), mChildPID(nullptr), mFromChildFD(nullptr),
mToChildFD(nullptr)
{
}
nsAuthSambaNTLM::~nsAuthSambaNTLM()
{
// ntlm_auth reads from stdin regularly so closing our file handles
// should cause it to exit.
Shutdown();
free(mInitialMessage);
}
void
nsAuthSambaNTLM::Shutdown()
{
if (mFromChildFD) {
PR_Close(mFromChildFD);
mFromChildFD = nullptr;
}
if (mToChildFD) {
PR_Close(mToChildFD);
mToChildFD = nullptr;
}
if (mChildPID) {
int32_t exitCode;
PR_WaitProcess(mChildPID, &exitCode);
mChildPID = nullptr;
}
}
NS_IMPL_ISUPPORTS(nsAuthSambaNTLM, nsIAuthModule)
static bool
SpawnIOChild(char* const* aArgs, PRProcess** aPID,
PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
{
PRFileDesc* toChildPipeRead;
PRFileDesc* toChildPipeWrite;
if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
return false;
PR_SetFDInheritable(toChildPipeRead, true);
PR_SetFDInheritable(toChildPipeWrite, false);
PRFileDesc* fromChildPipeRead;
PRFileDesc* fromChildPipeWrite;
if (PR_CreatePipe(&fromChildPipeRead, &fromChildPipeWrite) != PR_SUCCESS) {
PR_Close(toChildPipeRead);
PR_Close(toChildPipeWrite);
return false;
}
PR_SetFDInheritable(fromChildPipeRead, false);
PR_SetFDInheritable(fromChildPipeWrite, true);
PRProcessAttr* attr = PR_NewProcessAttr();
if (!attr) {
PR_Close(fromChildPipeRead);
PR_Close(fromChildPipeWrite);
PR_Close(toChildPipeRead);
PR_Close(toChildPipeWrite);
return false;
}
PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, toChildPipeRead);
PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, fromChildPipeWrite);
PRProcess* process = PR_CreateProcess(aArgs[0], aArgs, nullptr, attr);
PR_DestroyProcessAttr(attr);
PR_Close(fromChildPipeWrite);
PR_Close(toChildPipeRead);
if (!process) {
LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
PR_Close(fromChildPipeRead);
PR_Close(toChildPipeWrite);
return false;
}
*aPID = process;
*aFromChildFD = fromChildPipeRead;
*aToChildFD = toChildPipeWrite;
return true;
}
static bool WriteString(PRFileDesc* aFD, const nsACString& aString)
{
int32_t length = aString.Length();
const char* s = aString.BeginReading();
LOG(("Writing to ntlm_auth: %s", s));
while (length > 0) {
int result = PR_Write(aFD, s, length);
if (result <= 0)
return false;
s += result;
length -= result;
}
return true;
}
static bool ReadLine(PRFileDesc* aFD, nsACString& aString)
{
// ntlm_auth is defined to only send one line in response to each of our
// input lines. So this simple unbuffered strategy works as long as we
// read the response immediately after sending one request.
aString.Truncate();
for (;;) {
char buf[1024];
int result = PR_Read(aFD, buf, sizeof(buf));
if (result <= 0)
return false;
aString.Append(buf, result);
if (buf[result - 1] == '\n') {
LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get()));
return true;
}
}
}
/**
* Returns a heap-allocated array of PRUint8s, and stores the length in aLen.
* Returns nullptr if there's an error of any kind.
*/
static uint8_t* ExtractMessage(const nsACString& aLine, uint32_t* aLen)
{
// ntlm_auth sends blobs to us as base64-encoded strings after the "xx "
// preamble on the response line.
int32_t length = aLine.Length();
// The caller should verify there is a valid "xx " prefix and the line
// is terminated with a \n
NS_ASSERTION(length >= 4, "Line too short...");
const char* line = aLine.BeginReading();
const char* s = line + 3;
length -= 4; // lose first 3 chars plus trailing \n
NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated");
if (length & 3) {
// The base64 encoded block must be multiple of 4. If not, something
// screwed up.
NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
return nullptr;
}
// Calculate the exact length. I wonder why there isn't a function for this
// in plbase64.
int32_t numEquals;
for (numEquals = 0; numEquals < length; ++numEquals) {
if (s[length - 1 - numEquals] != '=')
break;
}
*aLen = (length/4)*3 - numEquals;
return reinterpret_cast<uint8_t*>(PL_Base64Decode(s, length, nullptr));
}
nsresult
nsAuthSambaNTLM::SpawnNTLMAuthHelper()
{
const char* username = PR_GetEnv("USER");
if (!username)
return NS_ERROR_FAILURE;
const char* const args[] = {
"ntlm_auth",
"--helper-protocol", "ntlmssp-client-1",
"--use-cached-creds",
"--username", username,
nullptr
};
bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID, &mFromChildFD, &mToChildFD);
if (!isOK)
return NS_ERROR_FAILURE;
if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
return NS_ERROR_FAILURE;
nsCString line;
if (!ReadLine(mFromChildFD, line))
return NS_ERROR_FAILURE;
if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) {
// Something went wrong. Perhaps no credentials are accessible.
return NS_ERROR_FAILURE;
}
// It gave us an initial client-to-server request packet. Save that
// because we'll need it later.
mInitialMessage = ExtractMessage(line, &mInitialMessageLen);
if (!mInitialMessage)
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
nsAuthSambaNTLM::Init(const char *serviceName,
uint32_t serviceFlags,
const char16_t *domain,
const char16_t *username,
const char16_t *password)
{
NS_ASSERTION(!username && !domain && !password, "unexpected credentials");
static bool sTelemetrySent = false;
if (!sTelemetrySent) {
mozilla::Telemetry::Accumulate(
mozilla::Telemetry::NTLM_MODULE_USED_2,
serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
? NTLM_MODULE_SAMBA_AUTH_PROXY
: NTLM_MODULE_SAMBA_AUTH_DIRECT);
sTelemetrySent = true;
}
return NS_OK;
}
NS_IMETHODIMP
nsAuthSambaNTLM::GetNextToken(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
if (!inToken) {
/* someone wants our initial message */
*outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen);
if (!*outToken)
return NS_ERROR_OUT_OF_MEMORY;
*outTokenLen = mInitialMessageLen;
return NS_OK;
}
/* inToken must be a type 2 message. Get ntlm_auth to generate our response */
char* encoded = PL_Base64Encode(static_cast<const char*>(inToken), inTokenLen, nullptr);
if (!encoded)
return NS_ERROR_OUT_OF_MEMORY;
nsCString request;
request.AssignLiteral("TT ");
request.Append(encoded);
free(encoded);
request.Append('\n');
if (!WriteString(mToChildFD, request))
return NS_ERROR_FAILURE;
nsCString line;
if (!ReadLine(mFromChildFD, line))
return NS_ERROR_FAILURE;
if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK ")) &&
!StringBeginsWith(line, NS_LITERAL_CSTRING("AF "))) {
// Something went wrong. Perhaps no credentials are accessible.
return NS_ERROR_FAILURE;
}
uint8_t* buf = ExtractMessage(line, outTokenLen);
if (!buf)
return NS_ERROR_FAILURE;
*outToken = nsMemory::Clone(buf, *outTokenLen);
free(buf);
if (!*outToken) {
return NS_ERROR_OUT_OF_MEMORY;
}
// We're done. Close our file descriptors now and reap the helper
// process.
Shutdown();
return NS_SUCCESS_AUTH_FINISHED;
}
NS_IMETHODIMP
nsAuthSambaNTLM::Unwrap(const void *inToken,
uint32_t inTokenLen,
void **outToken,
uint32_t *outTokenLen)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsAuthSambaNTLM::Wrap(const void *inToken,
uint32_t inTokenLen,
bool confidential,
void **outToken,
uint32_t *outTokenLen)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,52 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAuthSambaNTLM_h__
#define nsAuthSambaNTLM_h__
#include "nsIAuthModule.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "prio.h"
#include "prproces.h"
#include "mozilla/Attributes.h"
/**
* This is an implementation of NTLM authentication that does single-signon
* by obtaining the user's Unix username, parsing it into DOMAIN\name format,
* and then asking Samba's ntlm_auth tool to do the authentication for us
* using the user's password cached in winbindd, if available. If the
* password is not available then this component fails to instantiate so
* nsHttpNTLMAuth will fall back to a different NTLM implementation.
* NOTE: at time of writing, this requires patches to be added to the stock
* Samba winbindd and ntlm_auth!
*/
class nsAuthSambaNTLM final : public nsIAuthModule
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTHMODULE
nsAuthSambaNTLM();
// We spawn the ntlm_auth helper from the module constructor, because
// that lets us fail to instantiate the module if ntlm_auth isn't
// available, triggering fallback to the built-in NTLM support (which
// doesn't support single signon, of course)
nsresult SpawnNTLMAuthHelper();
private:
~nsAuthSambaNTLM();
void Shutdown();
uint8_t* mInitialMessage; /* free with free() */
uint32_t mInitialMessageLen;
PRProcess* mChildPID;
PRFileDesc* mFromChildFD;
PRFileDesc* mToChildFD;
};
#endif /* nsAuthSambaNTLM_h__ */

View file

@ -0,0 +1,731 @@
/* vim:set ts=4 sw=4 sts=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// HTTP Negotiate Authentication Support Module
//
// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
// (formerly draft-brezak-spnego-http-04.txt)
//
// Also described here:
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
//
#include <string.h>
#include <stdlib.h>
#include "nsAuth.h"
#include "nsHttpNegotiateAuth.h"
#include "nsIHttpAuthenticableChannel.h"
#include "nsIProxiedChannel.h"
#include "nsIAuthModule.h"
#include "nsIServiceManager.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIProxyInfo.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsNetCID.h"
#include "plbase64.h"
#include "plstr.h"
#include "mozilla/Base64.h"
#include "prprf.h"
#include "mozilla/Logging.h"
#include "prmem.h"
#include "prnetdb.h"
#include "mozilla/Likely.h"
#include "mozilla/Sprintf.h"
#include "nsIChannel.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsIHttpAuthenticatorCallback.h"
#include "mozilla/Mutex.h"
#include "nsICancelable.h"
using mozilla::Base64Decode;
//-----------------------------------------------------------------------------
static const char kNegotiate[] = "Negotiate";
static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
static const char kSSOinPBmode[] = "network.auth.private-browsing-sso";
#define kNegotiateLen (sizeof(kNegotiate)-1)
#define DEFAULT_THREAD_TIMEOUT_MS 30000
//-----------------------------------------------------------------------------
// Return false when the channel comes from a Private browsing window.
static bool
TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel, bool proxyAuth)
{
// Proxy should go all the time, it's not considered a privacy leak
// to send default credentials to a proxy.
if (proxyAuth) {
return true;
}
nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel);
MOZ_ASSERT(bareChannel);
if (!NS_UsePrivateBrowsing(bareChannel)) {
return true;
}
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
bool ssoInPb;
if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) && ssoInPb) {
return true;
}
// When the "Never remember history" option is set, all channels are
// set PB mode flag, but here we want to make an exception, users
// want their credentials go out.
bool dontRememberHistory;
if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart",
&dontRememberHistory)) &&
dontRememberHistory) {
return true;
}
}
return false;
}
NS_IMETHODIMP
nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags)
{
//
// Negotiate Auth creds should not be reused across multiple requests.
// Only perform the negotiation when it is explicitly requested by the
// server. Thus, do *NOT* use the "REUSABLE_CREDENTIALS" flag here.
//
// CONNECTION_BASED is specified instead of REQUEST_BASED since we need
// to complete a sequence of transactions with the server over the same
// connection.
//
*flags = CONNECTION_BASED | IDENTITY_IGNORED;
return NS_OK;
}
//
// Always set *identityInvalid == FALSE here. This
// will prevent the browser from popping up the authentication
// prompt window. Because GSSAPI does not have an API
// for fetching initial credentials (ex: A Kerberos TGT),
// there is no correct way to get the users credentials.
//
NS_IMETHODIMP
nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel,
const char *challenge,
bool isProxyAuth,
nsISupports **sessionState,
nsISupports **continuationState,
bool *identityInvalid)
{
nsIAuthModule *module = (nsIAuthModule *) *continuationState;
*identityInvalid = false;
if (module)
return NS_OK;
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = authChannel->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv))
return rv;
uint32_t req_flags = nsIAuthModule::REQ_DEFAULT;
nsAutoCString service;
if (isProxyAuth) {
if (!TestBoolPref(kNegotiateAuthAllowProxies)) {
LOG(("nsHttpNegotiateAuth::ChallengeReceived proxy auth blocked\n"));
return NS_ERROR_ABORT;
}
req_flags |= nsIAuthModule::REQ_PROXY_AUTH;
nsCOMPtr<nsIProxyInfo> proxyInfo;
authChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
NS_ENSURE_STATE(proxyInfo);
proxyInfo->GetHost(service);
}
else {
bool allowed = TestNotInPBMode(authChannel, isProxyAuth) &&
(TestNonFqdn(uri) ||
TestPref(uri, kNegotiateAuthTrustedURIs));
if (!allowed) {
LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
return NS_ERROR_ABORT;
}
bool delegation = TestPref(uri, kNegotiateAuthDelegationURIs);
if (delegation) {
LOG((" using REQ_DELEGATE\n"));
req_flags |= nsIAuthModule::REQ_DELEGATE;
}
rv = uri->GetAsciiHost(service);
if (NS_FAILED(rv))
return rv;
}
LOG((" service = %s\n", service.get()));
//
// The correct service name for IIS servers is "HTTP/f.q.d.n", so
// construct the proper service name for passing to "gss_import_name".
//
// TODO: Possibly make this a configurable service name for use
// with non-standard servers that use stuff like "khttp/f.q.d.n"
// instead.
//
service.Insert("HTTP@", 0);
const char *contractID;
if (TestBoolPref(kNegotiateAuthSSPI)) {
LOG((" using negotiate-sspi\n"));
contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi";
}
else {
LOG((" using negotiate-gss\n"));
contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss";
}
rv = CallCreateInstance(contractID, &module);
if (NS_FAILED(rv)) {
LOG((" Failed to load Negotiate Module \n"));
return rv;
}
rv = module->Init(service.get(), req_flags, nullptr, nullptr, nullptr);
if (NS_FAILED(rv)) {
NS_RELEASE(module);
return rv;
}
*continuationState = module;
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsHttpNegotiateAuth, nsIHttpAuthenticator)
namespace {
//
// GetNextTokenCompleteEvent
//
// This event is fired on main thread when async call of
// nsHttpNegotiateAuth::GenerateCredentials is finished. During the Run()
// method the nsIHttpAuthenticatorCallback::OnCredsAvailable is called with
// obtained credentials, flags and NS_OK when successful, otherwise
// NS_ERROR_FAILURE is returned as a result of failed operation.
//
class GetNextTokenCompleteEvent final : public nsIRunnable,
public nsICancelable
{
virtual ~GetNextTokenCompleteEvent()
{
if (mCreds) {
free(mCreds);
}
};
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit GetNextTokenCompleteEvent(nsIHttpAuthenticatorCallback* aCallback)
: mCallback(aCallback)
, mCreds(nullptr)
, mCancelled(false)
{
}
NS_IMETHODIMP DispatchSuccess(char *aCreds,
uint32_t aFlags,
already_AddRefed<nsISupports> aSessionState,
already_AddRefed<nsISupports> aContinuationState)
{
// Called from worker thread
MOZ_ASSERT(!NS_IsMainThread());
mCreds = aCreds;
mFlags = aFlags;
mResult = NS_OK;
mSessionState = aSessionState;
mContinuationState = aContinuationState;
return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP DispatchError(already_AddRefed<nsISupports> aSessionState,
already_AddRefed<nsISupports> aContinuationState)
{
// Called from worker thread
MOZ_ASSERT(!NS_IsMainThread());
mResult = NS_ERROR_FAILURE;
mSessionState = aSessionState;
mContinuationState = aContinuationState;
return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP Run() override
{
// Runs on main thread
MOZ_ASSERT(NS_IsMainThread());
if (!mCancelled) {
nsCOMPtr<nsIHttpAuthenticatorCallback> callback;
callback.swap(mCallback);
callback->OnCredsGenerated(mCreds, mFlags, mResult, mSessionState, mContinuationState);
}
return NS_OK;
}
NS_IMETHODIMP Cancel(nsresult aReason) override
{
// Supposed to be called from main thread
MOZ_ASSERT(NS_IsMainThread());
mCancelled = true;
return NS_OK;
}
private:
nsCOMPtr<nsIHttpAuthenticatorCallback> mCallback;
char *mCreds; // This class owns it, freed in destructor
uint32_t mFlags;
nsresult mResult;
bool mCancelled;
nsCOMPtr<nsISupports> mSessionState;
nsCOMPtr<nsISupports> mContinuationState;
};
NS_IMPL_ISUPPORTS(GetNextTokenCompleteEvent, nsIRunnable, nsICancelable)
//
// GetNextTokenRunnable
//
// This runnable is created by GenerateCredentialsAsync and it runs
// in nsHttpNegotiateAuth::mNegotiateThread and calling GenerateCredentials.
//
class GetNextTokenRunnable final : public mozilla::Runnable
{
virtual ~GetNextTokenRunnable() {}
public:
GetNextTokenRunnable(nsIHttpAuthenticableChannel *authChannel,
const char *challenge,
bool isProxyAuth,
const char16_t *domain,
const char16_t *username,
const char16_t *password,
nsISupports *sessionState,
nsISupports *continuationState,
GetNextTokenCompleteEvent *aCompleteEvent
)
: mAuthChannel(authChannel)
, mChallenge(challenge)
, mIsProxyAuth(isProxyAuth)
, mDomain(domain)
, mUsername(username)
, mPassword(password)
, mSessionState(sessionState)
, mContinuationState(continuationState)
, mCompleteEvent(aCompleteEvent)
{
}
NS_IMETHODIMP Run() override
{
// Runs on worker thread
MOZ_ASSERT(!NS_IsMainThread());
char *creds;
uint32_t flags;
nsresult rv = ObtainCredentialsAndFlags(&creds, &flags);
// Passing session and continuation state this way to not touch
// referencing of the object that may not be thread safe.
// Not having a thread safe referencing doesn't mean the object
// cannot be used on multiple threads (one example is nsAuthSSPI.)
// This ensures state objects will be destroyed on the main thread
// when not changed by GenerateCredentials.
if (NS_FAILED(rv)) {
return mCompleteEvent->DispatchError(mSessionState.forget(),
mContinuationState.forget());
}
return mCompleteEvent->DispatchSuccess(creds, flags,
mSessionState.forget(),
mContinuationState.forget());
}
NS_IMETHODIMP ObtainCredentialsAndFlags(char **aCreds, uint32_t *aFlags)
{
nsresult rv;
// Use negotiate service to call GenerateCredentials outside of main thread
nsAutoCString contractId;
contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
contractId.Append("negotiate");
nsCOMPtr<nsIHttpAuthenticator> authenticator =
do_GetService(contractId.get(), &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsISupports *sessionState = mSessionState;
nsISupports *continuationState = mContinuationState;
// The continuationState is for the sake of completeness propagated
// to the caller (despite it is not changed in any GenerateCredentials
// implementation).
//
// The only implementation that use sessionState is the
// nsHttpDigestAuth::GenerateCredentials. Since there's no reason
// to implement nsHttpDigestAuth::GenerateCredentialsAsync
// because digest auth does not block the main thread, we won't
// propagate changes to sessionState to the caller because of
// the change is too complicated on the caller side.
//
// Should any of the session or continuation states change inside
// this method, they must be threadsafe.
rv = authenticator->GenerateCredentials(mAuthChannel,
mChallenge.get(),
mIsProxyAuth,
mDomain.get(),
mUsername.get(),
mPassword.get(),
&sessionState,
&continuationState,
aFlags,
aCreds);
if (mSessionState != sessionState) {
mSessionState = sessionState;
}
if (mContinuationState != continuationState) {
mContinuationState = continuationState;
}
return rv;
}
private:
nsCOMPtr<nsIHttpAuthenticableChannel> mAuthChannel;
nsCString mChallenge;
bool mIsProxyAuth;
nsString mDomain;
nsString mUsername;
nsString mPassword;
nsCOMPtr<nsISupports> mSessionState;
nsCOMPtr<nsISupports> mContinuationState;
RefPtr<GetNextTokenCompleteEvent> mCompleteEvent;
};
} // anonymous namespace
NS_IMETHODIMP
nsHttpNegotiateAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel,
nsIHttpAuthenticatorCallback* aCallback,
const char *challenge,
bool isProxyAuth,
const char16_t *domain,
const char16_t *username,
const char16_t *password,
nsISupports *sessionState,
nsISupports *continuationState,
nsICancelable **aCancelable)
{
NS_ENSURE_ARG(aCallback);
NS_ENSURE_ARG_POINTER(aCancelable);
RefPtr<GetNextTokenCompleteEvent> cancelEvent =
new GetNextTokenCompleteEvent(aCallback);
nsCOMPtr<nsIRunnable> getNextTokenRunnable =
new GetNextTokenRunnable(authChannel,
challenge,
isProxyAuth,
domain,
username,
password,
sessionState,
continuationState,
cancelEvent);
cancelEvent.forget(aCancelable);
nsresult rv;
if (!mNegotiateThread) {
mNegotiateThread =
new mozilla::LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
NS_LITERAL_CSTRING("NegotiateAuth"));
NS_ENSURE_TRUE(mNegotiateThread, NS_ERROR_OUT_OF_MEMORY);
}
rv = mNegotiateThread->Dispatch(getNextTokenRunnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
//
// GenerateCredentials
//
// This routine is responsible for creating the correct authentication
// blob to pass to the server that requested "Negotiate" authentication.
//
NS_IMETHODIMP
nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
const char *challenge,
bool isProxyAuth,
const char16_t *domain,
const char16_t *username,
const char16_t *password,
nsISupports **sessionState,
nsISupports **continuationState,
uint32_t *flags,
char **creds)
{
// ChallengeReceived must have been called previously.
nsIAuthModule *module = (nsIAuthModule *) *continuationState;
NS_ENSURE_TRUE(module, NS_ERROR_NOT_INITIALIZED);
*flags = USING_INTERNAL_IDENTITY;
LOG(("nsHttpNegotiateAuth::GenerateCredentials() [challenge=%s]\n", challenge));
NS_ASSERTION(creds, "null param");
#ifdef DEBUG
bool isGssapiAuth =
!PL_strncasecmp(challenge, kNegotiate, kNegotiateLen);
NS_ASSERTION(isGssapiAuth, "Unexpected challenge");
#endif
//
// If the "Negotiate:" header had some data associated with it,
// that data should be used as the input to this call. This may
// be a continuation of an earlier call because GSSAPI authentication
// often takes multiple round-trips to complete depending on the
// context flags given. We want to use MUTUAL_AUTHENTICATION which
// generally *does* require multiple round-trips. Don't assume
// auth can be completed in just 1 call.
//
unsigned int len = strlen(challenge);
void *inToken, *outToken;
uint32_t inTokenLen, outTokenLen;
if (len > kNegotiateLen) {
challenge += kNegotiateLen;
while (*challenge == ' ')
challenge++;
len = strlen(challenge);
// strip off any padding (see bug 230351)
while (challenge[len - 1] == '=')
len--;
//
// Decode the response that followed the "Negotiate" token
//
nsresult rv =
Base64Decode(challenge, len, (char**)&inToken, &inTokenLen);
if (NS_FAILED(rv)) {
return rv;
}
}
else {
//
// Initializing, don't use an input token.
//
inToken = nullptr;
inTokenLen = 0;
}
nsresult rv = module->GetNextToken(inToken, inTokenLen, &outToken, &outTokenLen);
free(inToken);
if (NS_FAILED(rv))
return rv;
if (outTokenLen == 0) {
LOG((" No output token to send, exiting"));
return NS_ERROR_FAILURE;
}
//
// base64 encode the output token.
//
char *encoded_token = PL_Base64Encode((char *)outToken, outTokenLen, nullptr);
free(outToken);
if (!encoded_token)
return NS_ERROR_OUT_OF_MEMORY;
LOG((" Sending a token of length %d\n", outTokenLen));
// allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0")
const int bufsize = kNegotiateLen + 1 + strlen(encoded_token) + 1;
*creds = (char *) moz_xmalloc(bufsize);
if (MOZ_UNLIKELY(!*creds))
rv = NS_ERROR_OUT_OF_MEMORY;
else
snprintf(*creds, bufsize, "%s %s", kNegotiate, encoded_token);
PR_Free(encoded_token);
return rv;
}
bool
nsHttpNegotiateAuth::TestBoolPref(const char *pref)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return false;
bool val;
nsresult rv = prefs->GetBoolPref(pref, &val);
if (NS_FAILED(rv))
return false;
return val;
}
bool
nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri)
{
nsAutoCString host;
PRNetAddr addr;
if (!TestBoolPref(kNegotiateAuthAllowNonFqdn))
return false;
if (NS_FAILED(uri->GetAsciiHost(host)))
return false;
// return true if host does not contain a dot and is not an ip address
return !host.IsEmpty() && !host.Contains('.') &&
PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
}
bool
nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return false;
nsAutoCString scheme, host;
int32_t port;
if (NS_FAILED(uri->GetScheme(scheme)))
return false;
if (NS_FAILED(uri->GetAsciiHost(host)))
return false;
if (NS_FAILED(uri->GetPort(&port)))
return false;
char *hostList;
if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
return false;
// pseudo-BNF
// ----------
//
// url-list base-url ( base-url "," LWS )*
// base-url ( scheme-part | host-part | scheme-part host-part )
// scheme-part scheme "://"
// host-part host [":" port]
//
// for example:
// "https://, http://office.foo.com"
//
char *start = hostList, *end;
for (;;) {
// skip past any whitespace
while (*start == ' ' || *start == '\t')
++start;
end = strchr(start, ',');
if (!end)
end = start + strlen(start);
if (start == end)
break;
if (MatchesBaseURI(scheme, host, port, start, end))
return true;
if (*end == '\0')
break;
start = end + 1;
}
free(hostList);
return false;
}
bool
nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
const nsCSubstring &matchHost,
int32_t matchPort,
const char *baseStart,
const char *baseEnd)
{
// check if scheme://host:port matches baseURI
// parse the base URI
const char *hostStart, *schemeEnd = strstr(baseStart, "://");
if (schemeEnd) {
// the given scheme must match the parsed scheme exactly
if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
return false;
hostStart = schemeEnd + 3;
}
else
hostStart = baseStart;
// XXX this does not work for IPv6-literals
const char *hostEnd = strchr(hostStart, ':');
if (hostEnd && hostEnd < baseEnd) {
// the given port must match the parsed port exactly
int port = atoi(hostEnd + 1);
if (matchPort != (int32_t) port)
return false;
}
else
hostEnd = baseEnd;
// if we didn't parse out a host, then assume we got a match.
if (hostStart == hostEnd)
return true;
uint32_t hostLen = hostEnd - hostStart;
// matchHost must either equal host or be a subdomain of host
if (matchHost.Length() < hostLen)
return false;
const char *end = matchHost.EndReading();
if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
// if matchHost ends with host from the base URI, then make sure it is
// either an exact match, or prefixed with a dot. we don't want
// "foobar.com" to match "bar.com"
if (matchHost.Length() == hostLen ||
*(end - hostLen) == '.' ||
*(end - hostLen - 1) == '.')
return true;
}
return false;
}

View file

@ -0,0 +1,44 @@
/* vim:set ts=4 sw=4 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsHttpNegotiateAuth_h__
#define nsHttpNegotiateAuth_h__
#include "nsIHttpAuthenticator.h"
#include "nsIURI.h"
#include "nsSubstring.h"
#include "mozilla/Attributes.h"
#include "mozilla/LazyIdleThread.h"
// The nsHttpNegotiateAuth class provides responses for the GSS-API Negotiate method
// as specified by Microsoft in draft-brezak-spnego-http-04.txt
class nsHttpNegotiateAuth final : public nsIHttpAuthenticator
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIHTTPAUTHENTICATOR
private:
~nsHttpNegotiateAuth() {}
// returns the value of the given boolean pref
bool TestBoolPref(const char *pref);
// tests if the host part of an uri is fully qualified
bool TestNonFqdn(nsIURI *uri);
// returns true if URI is accepted by the list of hosts in the pref
bool TestPref(nsIURI *, const char *pref);
bool MatchesBaseURI(const nsCSubstring &scheme,
const nsCSubstring &host,
int32_t port,
const char *baseStart,
const char *baseEnd);
// Thread for GenerateCredentialsAsync
RefPtr<mozilla::LazyIdleThread> mNegotiateThread;
};
#endif /* nsHttpNegotiateAuth_h__ */

18
extensions/build.mk Normal file
View file

@ -0,0 +1,18 @@
# 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/.
ifneq (extensions,$(MOZ_BUILD_APP))
$(error This file shouldn't be included without --enable-application=extensions)
endif
ifndef MOZ_EXTENSIONS
$(error You forgot to set --enable-extensions)
endif
TIERS += app
tier_app_dirs += extensions
installer:
@echo Check each extension for an installer.
@exit 1

8
extensions/confvars.sh Normal file
View file

@ -0,0 +1,8 @@
#! /bin/sh
# 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/.
MOZ_APP_NAME=mozilla
MOZ_APP_DISPLAYNAME=Mozilla
MOZ_APP_VERSION=$MOZILLA_VERSION

View file

@ -0,0 +1,30 @@
# -*- 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/.
TEST_DIRS += ['test']
XPIDL_SOURCES += [
'nsICookieAcceptDialog.idl',
'nsICookiePromptService.idl',
]
XPIDL_MODULE = 'cookie'
UNIFIED_SOURCES += [
'nsCookieModule.cpp',
'nsCookiePermission.cpp',
'nsCookiePromptService.cpp',
'nsPermission.cpp',
'nsPermissionManager.cpp',
'nsPopupWindowManager.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ModuleUtils.h"
#include "nsIServiceManager.h"
#include "nsPermissionManager.h"
#include "nsPopupWindowManager.h"
#include "nsICategoryManager.h"
#include "nsCookiePromptService.h"
#include "nsCookiePermission.h"
#include "nsXPIDLString.h"
// Define the constructor function for the objects
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPermissionManager,
nsPermissionManager::GetXPCOMSingleton)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPopupWindowManager, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookiePermission)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCookiePromptService)
NS_DEFINE_NAMED_CID(NS_PERMISSIONMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_POPUPWINDOWMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_COOKIEPROMPTSERVICE_CID);
NS_DEFINE_NAMED_CID(NS_COOKIEPERMISSION_CID);
static const mozilla::Module::CIDEntry kCookieCIDs[] = {
{ &kNS_PERMISSIONMANAGER_CID, false, nullptr, nsIPermissionManagerConstructor },
{ &kNS_POPUPWINDOWMANAGER_CID, false, nullptr, nsPopupWindowManagerConstructor },
{ &kNS_COOKIEPROMPTSERVICE_CID, false, nullptr, nsCookiePromptServiceConstructor },
{ &kNS_COOKIEPERMISSION_CID, false, nullptr, nsCookiePermissionConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kCookieContracts[] = {
{ NS_PERMISSIONMANAGER_CONTRACTID, &kNS_PERMISSIONMANAGER_CID },
{ NS_POPUPWINDOWMANAGER_CONTRACTID, &kNS_POPUPWINDOWMANAGER_CID },
{ NS_COOKIEPROMPTSERVICE_CONTRACTID, &kNS_COOKIEPROMPTSERVICE_CID },
{ NS_COOKIEPERMISSION_CONTRACTID, &kNS_COOKIEPERMISSION_CID },
{ nullptr }
};
static const mozilla::Module kCookieModule = {
mozilla::Module::kVersion,
kCookieCIDs,
kCookieContracts
};
NSMODULE_DEFN(nsCookieModule) = &kCookieModule;

View file

@ -0,0 +1,273 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et: */
/* 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 "nsCookiePermission.h"
#include "mozIThirdPartyUtil.h"
#include "nsICookie2.h"
#include "nsIServiceManager.h"
#include "nsICookiePromptService.h"
#include "nsICookieManager2.h"
#include "nsNetUtil.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIProtocolHandler.h"
#include "nsIURI.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIDOMWindow.h"
#include "nsIPrincipal.h"
#include "nsString.h"
#include "nsCRT.h"
#include "nsILoadContext.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsNetCID.h"
#include "prtime.h"
/****************************************************************
************************ nsCookiePermission ********************
****************************************************************/
// values for mCookiesLifetimePolicy
// 0 == accept normally
// 1 == ask before accepting, no more supported, treated like ACCEPT_NORMALLY (Bug 606655).
// 2 == downgrade to session
// 3 == limit lifetime to N days
static const uint32_t ACCEPT_NORMALLY = 0;
static const uint32_t ASK_BEFORE_ACCEPT = 1;
static const uint32_t ACCEPT_SESSION = 2;
static const uint32_t ACCEPT_FOR_N_DAYS = 3;
static const bool kDefaultPolicy = true;
static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
// obsolete pref names for migration
static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
static const char kPermissionType[] = "cookie";
NS_IMPL_ISUPPORTS(nsCookiePermission,
nsICookiePermission,
nsIObserver)
bool
nsCookiePermission::Init()
{
// Initialize nsIPermissionManager and fetch relevant prefs. This is only
// required for some methods on nsICookiePermission, so it should be done
// lazily.
nsresult rv;
mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv)) return false;
mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
if (NS_FAILED(rv)) return false;
// failure to access the pref service is non-fatal...
nsCOMPtr<nsIPrefBranch> prefBranch =
do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefBranch) {
prefBranch->AddObserver(kCookiesLifetimePolicy, this, false);
prefBranch->AddObserver(kCookiesLifetimeDays, this, false);
PrefChanged(prefBranch, nullptr);
// migration code for original cookie prefs
bool migrated;
rv = prefBranch->GetBoolPref(kCookiesPrefsMigrated, &migrated);
if (NS_FAILED(rv) || !migrated) {
bool lifetimeEnabled = false;
prefBranch->GetBoolPref(kCookiesLifetimeEnabled, &lifetimeEnabled);
// if they're limiting lifetime, use the appropriate limited lifetime pref
if (lifetimeEnabled) {
int32_t lifetimeBehavior;
prefBranch->GetIntPref(kCookiesLifetimeBehavior, &lifetimeBehavior);
if (lifetimeBehavior)
prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_FOR_N_DAYS);
else
prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_SESSION);
}
prefBranch->SetBoolPref(kCookiesPrefsMigrated, true);
}
}
return true;
}
void
nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
const char *aPref)
{
int32_t val;
#define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
if (PREF_CHANGED(kCookiesLifetimePolicy) &&
NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val))) {
if (val != static_cast<int32_t>(ACCEPT_SESSION) && val != static_cast<int32_t>(ACCEPT_FOR_N_DAYS)) {
val = ACCEPT_NORMALLY;
}
mCookiesLifetimePolicy = val;
}
if (PREF_CHANGED(kCookiesLifetimeDays) &&
NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val)))
// save cookie lifetime in seconds instead of days
mCookiesLifetimeSec = (int64_t)val * 24 * 60 * 60;
}
NS_IMETHODIMP
nsCookiePermission::SetAccess(nsIURI *aURI,
nsCookieAccess aAccess)
{
// Lazily initialize ourselves
if (!EnsureInitialized())
return NS_ERROR_UNEXPECTED;
//
// NOTE: nsCookieAccess values conveniently match up with
// the permission codes used by nsIPermissionManager.
// this is nice because it avoids conversion code.
//
return mPermMgr->Add(aURI, kPermissionType, aAccess,
nsIPermissionManager::EXPIRE_NEVER, 0);
}
NS_IMETHODIMP
nsCookiePermission::CanAccess(nsIURI *aURI,
nsIChannel *aChannel,
nsCookieAccess *aResult)
{
// Check this protocol doesn't allow cookies
bool hasFlags;
nsresult rv =
NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
&hasFlags);
if (NS_FAILED(rv) || hasFlags) {
*aResult = ACCESS_DENY;
return NS_OK;
}
// Lazily initialize ourselves
if (!EnsureInitialized())
return NS_ERROR_UNEXPECTED;
// finally, check with permission manager...
rv = mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *) aResult);
if (NS_SUCCEEDED(rv)) {
if (*aResult == nsICookiePermission::ACCESS_SESSION) {
*aResult = nsICookiePermission::ACCESS_ALLOW;
}
}
return rv;
}
NS_IMETHODIMP
nsCookiePermission::CanSetCookie(nsIURI *aURI,
nsIChannel *aChannel,
nsICookie2 *aCookie,
bool *aIsSession,
int64_t *aExpiry,
bool *aResult)
{
NS_ASSERTION(aURI, "null uri");
*aResult = kDefaultPolicy;
// Lazily initialize ourselves
if (!EnsureInitialized())
return NS_ERROR_UNEXPECTED;
uint32_t perm;
mPermMgr->TestPermission(aURI, kPermissionType, &perm);
bool isThirdParty = false;
switch (perm) {
case nsICookiePermission::ACCESS_SESSION:
*aIsSession = true;
MOZ_FALLTHROUGH;
case nsICookiePermission::ACCESS_ALLOW:
*aResult = true;
break;
case nsICookiePermission::ACCESS_DENY:
*aResult = false;
break;
case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
// If it's third party, we can't set the cookie
if (isThirdParty)
*aResult = false;
break;
case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
// If it's third party, check whether cookies are already set
if (isThirdParty) {
nsresult rv;
nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
*aResult = false;
break;
}
uint32_t priorCookieCount = 0;
nsAutoCString hostFromURI;
aURI->GetHost(hostFromURI);
cookieManager->CountCookiesFromHost(hostFromURI, &priorCookieCount);
*aResult = priorCookieCount != 0;
}
break;
default:
// the permission manager has nothing to say about this cookie -
// so, we apply the default prefs to it.
NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
// now we need to figure out what type of accept policy we're dealing with
// if we accept cookies normally, just bail and return
if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
*aResult = true;
return NS_OK;
}
// declare this here since it'll be used in all of the remaining cases
int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
int64_t delta = *aExpiry - currentTime;
// We are accepting the cookie, but,
// if it's not a session cookie, we may have to limit its lifetime.
if (!*aIsSession && delta > 0) {
if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
// limit lifetime to session
*aIsSession = true;
} else if (delta > mCookiesLifetimeSec) {
// limit lifetime to specified time
*aExpiry = currentTime + mCookiesLifetimeSec;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCookiePermission::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
"unexpected topic - we only deal with pref changes!");
if (prefBranch)
PrefChanged(prefBranch, NS_LossyConvertUTF16toASCII(aData).get());
return NS_OK;
}

View file

@ -0,0 +1,48 @@
/* 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 nsCookiePermission_h__
#define nsCookiePermission_h__
#include "nsICookiePermission.h"
#include "nsIPermissionManager.h"
#include "nsIObserver.h"
#include "nsCOMPtr.h"
#include "mozIThirdPartyUtil.h"
class nsIPrefBranch;
class nsCookiePermission : public nsICookiePermission
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICOOKIEPERMISSION
NS_DECL_NSIOBSERVER
nsCookiePermission()
: mCookiesLifetimeSec(INT64_MAX)
, mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY
{}
bool Init();
void PrefChanged(nsIPrefBranch *, const char *);
private:
virtual ~nsCookiePermission() {}
bool EnsureInitialized() { return (mPermMgr != nullptr && mThirdPartyUtil != nullptr) || Init(); };
nsCOMPtr<nsIPermissionManager> mPermMgr;
nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
int64_t mCookiesLifetimeSec; // lifetime limit specified in seconds
uint8_t mCookiesLifetimePolicy; // pref for how long cookies are stored
};
// {EF565D0A-AB9A-4A13-9160-0644CDFD859A}
#define NS_COOKIEPERMISSION_CID \
{0xEF565D0A, 0xAB9A, 0x4A13, {0x91, 0x60, 0x06, 0x44, 0xcd, 0xfd, 0x85, 0x9a }}
#endif

View file

@ -0,0 +1,101 @@
/* 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 "nsCookiePromptService.h"
#include "nsICookie.h"
#include "nsICookieAcceptDialog.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIWindowWatcher.h"
#include "nsIServiceManager.h"
#include "nsString.h"
#include "nsIDialogParamBlock.h"
#include "nsIMutableArray.h"
#include "mozilla/dom/ScriptSettings.h"
/****************************************************************
************************ nsCookiePromptService *****************
****************************************************************/
NS_IMPL_ISUPPORTS(nsCookiePromptService, nsICookiePromptService)
nsCookiePromptService::nsCookiePromptService() {
}
nsCookiePromptService::~nsCookiePromptService() {
}
NS_IMETHODIMP
nsCookiePromptService::CookieDialog(mozIDOMWindowProxy *aParent,
nsICookie *aCookie,
const nsACString &aHostname,
int32_t aCookiesFromHost,
bool aChangingCookie,
bool *aRememberDecision,
int32_t *aAccept)
{
nsresult rv;
nsCOMPtr<nsIDialogParamBlock> block = do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID,&rv);
if (NS_FAILED(rv)) return rv;
block->SetInt(nsICookieAcceptDialog::ACCEPT_COOKIE, 1);
block->SetString(nsICookieAcceptDialog::HOSTNAME, NS_ConvertUTF8toUTF16(aHostname).get());
block->SetInt(nsICookieAcceptDialog::COOKIESFROMHOST, aCookiesFromHost);
block->SetInt(nsICookieAcceptDialog::CHANGINGCOOKIE, aChangingCookie ? 1 : 0);
nsCOMPtr<nsIMutableArray> objects =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = objects->AppendElement(aCookie, false);
if (NS_FAILED(rv)) return rv;
block->SetObjects(objects);
nsCOMPtr<nsIWindowWatcher> wwatcher = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISupports> arguments = do_QueryInterface(block);
nsCOMPtr<mozIDOMWindowProxy> parent(aParent);
if (!parent) // if no parent provided, consult the window watcher:
wwatcher->GetActiveWindow(getter_AddRefs(parent));
if (parent) {
auto* privateParent = nsPIDOMWindowOuter::From(parent);
if (privateParent)
privateParent = privateParent->GetPrivateRoot();
parent = privateParent;
}
// We're opening a chrome window and passing in a nsIDialogParamBlock. Setting
// the nsIDialogParamBlock as the .arguments property on the chrome window
// requires system principals on the stack, so we use an AutoNoJSAPI for that.
mozilla::dom::AutoNoJSAPI nojsapi;
// The cookie dialog will be modal for the root chrome window rather than the
// tab containing the permission-requesting page. This removes confusion
// about which monitor is displaying the dialog (see bug 470356), but also
// avoids unwanted tab switches (see bug 405239).
nsCOMPtr<mozIDOMWindowProxy> dialog;
rv = wwatcher->OpenWindow(parent, "chrome://cookie/content/cookieAcceptDialog.xul", "_blank",
"centerscreen,chrome,modal,titlebar", arguments,
getter_AddRefs(dialog));
if (NS_FAILED(rv)) return rv;
// get back output parameters
int32_t tempValue;
block->GetInt(nsICookieAcceptDialog::ACCEPT_COOKIE, &tempValue);
*aAccept = tempValue;
// GetInt returns a int32_t; we need to sanitize it into bool
block->GetInt(nsICookieAcceptDialog::REMEMBER_DECISION, &tempValue);
*aRememberDecision = (tempValue == 1);
return rv;
}

View file

@ -0,0 +1,29 @@
/* 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 nsCookiePromptService_h__
#define nsCookiePromptService_h__
#include "nsICookiePromptService.h"
class nsCookiePromptService : public nsICookiePromptService {
virtual ~nsCookiePromptService();
public:
nsCookiePromptService();
NS_DECL_NSICOOKIEPROMPTSERVICE
NS_DECL_ISUPPORTS
private:
};
// {CE002B28-92B7-4701-8621-CC925866FB87}
#define NS_COOKIEPROMPTSERVICE_CID \
{0xCE002B28, 0x92B7, 0x4701, {0x86, 0x21, 0xCC, 0x92, 0x58, 0x66, 0xFB, 0x87}}
#endif

View file

@ -0,0 +1,21 @@
/* 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 "nsISupports.idl"
/*
This file contains some constants for the cookie accept dialog
*/
[scriptable, uuid(3F2F0D2C-BDEA-4B5A-AFC6-FCF18F66B97E)]
interface nsICookieAcceptDialog: nsISupports {
const short ACCEPT_COOKIE=0;
const short REMEMBER_DECISION=1;
const short HOSTNAME=2;
const short COOKIESFROMHOST=3;
const short CHANGINGCOOKIE=4;
};

View file

@ -0,0 +1,45 @@
/* 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 "nsISupports.idl"
/**
* An interface to open a dialog to ask to permission to accept the cookie.
*/
interface mozIDOMWindowProxy;
interface nsICookie;
[scriptable, uuid(65ca07c3-6241-4de1-bf41-3336470499db)]
interface nsICookiePromptService : nsISupports
{
const uint32_t DENY_COOKIE = 0;
const uint32_t ACCEPT_COOKIE = 1;
const uint32_t ACCEPT_SESSION_COOKIE = 2;
/* Open a dialog that asks for permission to accept a cookie
*
* @param parent
* @param cookie
* @param hostname the host that wants to set the cookie,
* not the domain: part of the cookie
* @param cookiesFromHost the number of cookies there are already for this host
* @param changingCookie are we changing this cookie?
* @param rememberDecision should we set the matching permission for this host?
* @returns 0 == deny cookie
* 1 == accept cookie
* 2 == accept cookie for current session
*/
long cookieDialog(in mozIDOMWindowProxy parent,
in nsICookie cookie,
in ACString hostname,
in long cookiesFromHost,
in boolean changingCookie,
out boolean rememberDecision);
};
%{C++
#define NS_COOKIEPROMPTSERVICE_CONTRACTID "@mozilla.org/embedcomp/cookieprompt-service;1"
%}

View file

@ -0,0 +1,201 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsPermission.h"
#include "nsContentUtils.h"
#include "nsIClassInfoImpl.h"
#include "nsIEffectiveTLDService.h"
#include "mozilla/BasePrincipal.h"
// nsPermission Implementation
NS_IMPL_CLASSINFO(nsPermission, nullptr, 0, {0})
NS_IMPL_ISUPPORTS_CI(nsPermission, nsIPermission)
nsPermission::nsPermission(nsIPrincipal* aPrincipal,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime)
: mPrincipal(aPrincipal)
, mType(aType)
, mCapability(aCapability)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime)
{
}
already_AddRefed<nsPermission>
nsPermission::Create(nsIPrincipal* aPrincipal,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime)
{
NS_ENSURE_TRUE(aPrincipal, nullptr);
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::Cast(aPrincipal)->CloneStrippingUserContextIdAndFirstPartyDomain();
NS_ENSURE_TRUE(principal, nullptr);
RefPtr<nsPermission> permission =
new nsPermission(principal, aType, aCapability, aExpireType, aExpireTime);
return permission.forget();
}
NS_IMETHODIMP
nsPermission::GetPrincipal(nsIPrincipal** aPrincipal)
{
nsCOMPtr<nsIPrincipal> copy = mPrincipal;
copy.forget(aPrincipal);
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetType(nsACString &aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetCapability(uint32_t *aCapability)
{
*aCapability = mCapability;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetExpireType(uint32_t *aExpireType)
{
*aExpireType = mExpireType;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetExpireTime(int64_t *aExpireTime)
{
*aExpireTime = mExpireTime;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aMatches);
*aMatches = false;
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::Cast(aPrincipal)->CloneStrippingUserContextIdAndFirstPartyDomain();
if (!principal) {
*aMatches = false;
return NS_OK;
}
// If the principals are equal, then they match.
if (mPrincipal->Equals(principal)) {
*aMatches = true;
return NS_OK;
}
// If we are matching with an exact host, we're done now - the permissions don't match
// otherwise, we need to start comparing subdomains!
if (aExactHost) {
return NS_OK;
}
// Compare their OriginAttributes
const mozilla::PrincipalOriginAttributes& theirAttrs = mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef();
const mozilla::PrincipalOriginAttributes& ourAttrs = mozilla::BasePrincipal::Cast(mPrincipal)->OriginAttributesRef();
if (theirAttrs != ourAttrs) {
return NS_OK;
}
nsCOMPtr<nsIURI> theirURI;
nsresult rv = principal->GetURI(getter_AddRefs(theirURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> ourURI;
rv = mPrincipal->GetURI(getter_AddRefs(ourURI));
NS_ENSURE_SUCCESS(rv, rv);
// Compare schemes
nsAutoCString theirScheme;
rv = theirURI->GetScheme(theirScheme);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString ourScheme;
rv = ourURI->GetScheme(ourScheme);
NS_ENSURE_SUCCESS(rv, rv);
if (theirScheme != ourScheme) {
return NS_OK;
}
// Compare ports
int32_t theirPort;
rv = theirURI->GetPort(&theirPort);
NS_ENSURE_SUCCESS(rv, rv);
int32_t ourPort;
rv = ourURI->GetPort(&ourPort);
NS_ENSURE_SUCCESS(rv, rv);
if (theirPort != ourPort) {
return NS_OK;
}
// Check if the host or any subdomain of their host matches.
nsAutoCString theirHost;
rv = theirURI->GetHost(theirHost);
if (NS_FAILED(rv) || theirHost.IsEmpty()) {
return NS_OK;
}
nsAutoCString ourHost;
rv = ourURI->GetHost(ourHost);
if (NS_FAILED(rv) || ourHost.IsEmpty()) {
return NS_OK;
}
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
NS_ERROR("Should have a tld service!");
return NS_ERROR_FAILURE;
}
// This loop will not loop forever, as GetNextSubDomain will eventually fail
// with NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
while (theirHost != ourHost) {
rv = tldService->GetNextSubDomain(theirHost, theirHost);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
return NS_OK;
} else {
return rv;
}
}
}
*aMatches = true;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::MatchesURI(nsIURI* aURI, bool aExactHost, bool* aMatches)
{
NS_ENSURE_ARG_POINTER(aURI);
mozilla::PrincipalOriginAttributes attrs;
nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
return Matches(principal, aExactHost, aMatches);
}

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 nsPermission_h__
#define nsPermission_h__
#include "nsIPermission.h"
#include "nsString.h"
////////////////////////////////////////////////////////////////////////////////
class nsPermission : public nsIPermission
{
public:
// nsISupports
NS_DECL_ISUPPORTS
NS_DECL_NSIPERMISSION
static already_AddRefed<nsPermission> Create(nsIPrincipal* aPrincipal,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime);
protected:
nsPermission(nsIPrincipal* aPrincipal,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime);
virtual ~nsPermission() {};
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCString mType;
uint32_t mCapability;
uint32_t mExpireType;
int64_t mExpireTime;
};
#endif // nsPermission_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,295 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 nsPermissionManager_h__
#define nsPermissionManager_h__
#include "nsIPermissionManager.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsTHashtable.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsPermission.h"
#include "nsHashKeys.h"
#include "nsCOMArray.h"
#include "nsDataHashtable.h"
namespace mozilla {
class OriginAttributesPattern;
}
class nsIPermission;
class mozIStorageConnection;
class mozIStorageAsyncStatement;
////////////////////////////////////////////////////////////////////////////////
class nsPermissionManager final : public nsIPermissionManager,
public nsIObserver,
public nsSupportsWeakReference
{
public:
class PermissionEntry
{
public:
PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime,
int64_t aModificationTime)
: mID(aID)
, mType(aType)
, mPermission(aPermission)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime)
, mModificationTime(aModificationTime)
, mNonSessionPermission(aPermission)
, mNonSessionExpireType(aExpireType)
, mNonSessionExpireTime(aExpireTime)
{}
int64_t mID;
uint32_t mType;
uint32_t mPermission;
uint32_t mExpireType;
int64_t mExpireTime;
int64_t mModificationTime;
uint32_t mNonSessionPermission;
uint32_t mNonSessionExpireType;
uint32_t mNonSessionExpireTime;
};
/**
* PermissionKey is the key used by PermissionHashKey hash table.
*
* NOTE: It could be implementing nsIHashable but there is no reason to worry
* with XPCOM interfaces while we don't need to.
*/
class PermissionKey
{
public:
explicit PermissionKey(nsIPrincipal* aPrincipal);
explicit PermissionKey(const nsACString& aOrigin)
: mOrigin(aOrigin)
{
}
bool operator==(const PermissionKey& aKey) const {
return mOrigin.Equals(aKey.mOrigin);
}
PLDHashNumber GetHashCode() const {
return mozilla::HashString(mOrigin);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey)
nsCString mOrigin;
private:
// Default ctor shouldn't be used.
PermissionKey() = delete;
// Dtor shouldn't be used outside of the class.
~PermissionKey() {};
};
class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
{
public:
explicit PermissionHashKey(const PermissionKey* aPermissionKey)
: nsRefPtrHashKey<PermissionKey>(aPermissionKey)
{}
PermissionHashKey(const PermissionHashKey& toCopy)
: nsRefPtrHashKey<PermissionKey>(toCopy)
, mPermissions(toCopy.mPermissions)
{}
bool KeyEquals(const PermissionKey* aKey) const
{
return *aKey == *GetKey();
}
static PLDHashNumber HashKey(const PermissionKey* aKey)
{
return aKey->GetHashCode();
}
// Force the hashtable to use the copy constructor when shuffling entries
// around, otherwise the Auto part of our AutoTArray won't be happy!
enum { ALLOW_MEMMOVE = false };
inline nsTArray<PermissionEntry> & GetPermissions()
{
return mPermissions;
}
inline int32_t GetPermissionIndex(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
inline PermissionEntry GetPermission(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return mPermissions[i];
// unknown permission... return relevant data
return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
nsIPermissionManager::EXPIRE_NEVER, 0, 0);
}
private:
AutoTArray<PermissionEntry, 1> mPermissions;
};
// nsISupports
NS_DECL_ISUPPORTS
NS_DECL_NSIPERMISSIONMANAGER
NS_DECL_NSIOBSERVER
nsPermissionManager();
static nsIPermissionManager* GetXPCOMSingleton();
nsresult Init();
// enums for AddInternal()
enum OperationType {
eOperationNone,
eOperationAdding,
eOperationRemoving,
eOperationChanging,
eOperationReplacingDefault
};
enum DBOperationType {
eNoDBOperation,
eWriteToDB
};
enum NotifyOperationType {
eDontNotify,
eNotify
};
// A special value for a permission ID that indicates the ID was loaded as
// a default value. These will never be written to the database, but may
// be overridden with an explicit permission (including UNKNOWN_ACTION)
static const int64_t cIDPermissionIsDefault = -1;
nsresult AddInternal(nsIPrincipal* aPrincipal,
const nsAFlatCString &aType,
uint32_t aPermission,
int64_t aID,
uint32_t aExpireType,
int64_t aExpireTime,
int64_t aModificationTime,
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation,
const bool aIgnoreSessionPermissions = false);
/**
* Initialize the "clear-origin-attributes-data" observing.
* Will create a nsPermissionManager instance if needed.
* That way, we can prevent have nsPermissionManager created at startup just
* to be able to clear data when an application is uninstalled.
*/
static void ClearOriginDataObserverInit();
nsresult
RemovePermissionsWithAttributes(mozilla::OriginAttributesPattern& aAttrs);
private:
virtual ~nsPermissionManager();
int32_t GetTypeIndex(const char *aTypeString,
bool aAdd);
PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal,
uint32_t aType,
bool aExactHostMatch);
nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
uint32_t *aPermission,
bool aExactHostMatch,
bool aIncludingSession);
nsresult OpenDatabase(nsIFile* permissionsFile);
nsresult InitDB(bool aRemoveFile);
nsresult CreateTable();
nsresult Import();
nsresult ImportDefaults();
nsresult _DoImport(nsIInputStream *inputStream, mozIStorageConnection *aConn);
nsresult Read();
void NotifyObserversWithPermission(nsIPrincipal* aPrincipal,
const nsCString &aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime,
const char16_t *aData);
void NotifyObservers(nsIPermission *aPermission, const char16_t *aData);
// Finalize all statements, close the DB and null it.
// if aRebuildOnSuccess, reinitialize database
void CloseDB(bool aRebuildOnSuccess = false);
nsresult RemoveAllInternal(bool aNotifyObservers);
nsresult RemoveAllFromMemory();
static void UpdateDB(OperationType aOp,
mozIStorageAsyncStatement* aStmt,
int64_t aID,
const nsACString& aOrigin,
const nsACString& aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime,
int64_t aModificationTime);
/**
* This method removes all permissions modified after the specified time.
*/
nsresult
RemoveAllModifiedSince(int64_t aModificationTime);
/**
* Retrieve permissions from chrome process.
*/
nsresult
FetchPermissions();
nsCOMPtr<mozIStorageConnection> mDBConn;
nsCOMPtr<mozIStorageAsyncStatement> mStmtInsert;
nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete;
nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate;
bool mMemoryOnlyDB;
nsTHashtable<PermissionHashKey> mPermissionTable;
// a unique, monotonically increasing id used to identify each database entry
int64_t mLargestID;
// An array to store the strings identifying the different types.
nsTArray<nsCString> mTypeArray;
// Initially, |false|. Set to |true| once shutdown has started, to avoid
// reopening the database.
bool mIsShuttingDown;
friend class DeleteFromMozHostListener;
friend class CloseDatabaseListener;
};
// {4F6B5E00-0C36-11d5-A535-0010A401EB10}
#define NS_PERMISSIONMANAGER_CID \
{ 0x4f6b5e00, 0xc36, 0x11d5, { 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 } }
#endif /* nsPermissionManager_h__ */

View file

@ -0,0 +1,111 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsPopupWindowManager.h"
#include "nsCRT.h"
#include "nsIServiceManager.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "mozilla/Services.h"
/**
* The Popup Window Manager maintains popup window permissions by website.
*/
static const char kPopupDisablePref[] = "dom.disable_open_during_load";
//*****************************************************************************
//*** nsPopupWindowManager object management and nsISupports
//*****************************************************************************
nsPopupWindowManager::nsPopupWindowManager() :
mPolicy(ALLOW_POPUP)
{
}
nsPopupWindowManager::~nsPopupWindowManager()
{
}
NS_IMPL_ISUPPORTS(nsPopupWindowManager,
nsIPopupWindowManager,
nsIObserver,
nsISupportsWeakReference)
nsresult
nsPopupWindowManager::Init()
{
nsresult rv;
mPermissionManager = mozilla::services::GetPermissionManager();
nsCOMPtr<nsIPrefBranch> prefBranch =
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
bool permission;
rv = prefBranch->GetBoolPref(kPopupDisablePref, &permission);
if (NS_FAILED(rv)) {
permission = true;
}
mPolicy = permission ? (uint32_t) DENY_POPUP : (uint32_t) ALLOW_POPUP;
prefBranch->AddObserver(kPopupDisablePref, this, true);
}
return NS_OK;
}
//*****************************************************************************
//*** nsPopupWindowManager::nsIPopupWindowManager
//*****************************************************************************
NS_IMETHODIMP
nsPopupWindowManager::TestPermission(nsIPrincipal* aPrincipal,
uint32_t *aPermission)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aPermission);
uint32_t permit;
*aPermission = mPolicy;
if (mPermissionManager) {
if (NS_SUCCEEDED(mPermissionManager->TestPermissionFromPrincipal(aPrincipal, "popup", &permit))) {
// Share some constants between interfaces?
if (permit == nsIPermissionManager::ALLOW_ACTION) {
*aPermission = ALLOW_POPUP;
} else if (permit == nsIPermissionManager::DENY_ACTION) {
*aPermission = DENY_POPUP;
}
}
}
return NS_OK;
}
//*****************************************************************************
//*** nsPopupWindowManager::nsIObserver
//*****************************************************************************
NS_IMETHODIMP
nsPopupWindowManager::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
"unexpected topic - we only deal with pref changes!");
if (prefBranch) {
// refresh our local copy of the "disable popups" pref
bool permission = true;
prefBranch->GetBoolPref(kPopupDisablePref, &permission);
mPolicy = permission ? (uint32_t) DENY_POPUP : (uint32_t) ALLOW_POPUP;
}
return NS_OK;
}

View file

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 nsPopupWindowManager_h__
#define nsPopupWindowManager_h__
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsIPermissionManager.h"
#include "nsIPopupWindowManager.h"
#include "nsWeakReference.h"
class nsPopupWindowManager : public nsIPopupWindowManager,
public nsIObserver,
public nsSupportsWeakReference {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPOPUPWINDOWMANAGER
NS_DECL_NSIOBSERVER
nsPopupWindowManager();
nsresult Init();
private:
virtual ~nsPopupWindowManager();
uint32_t mPolicy;
nsCOMPtr<nsIPermissionManager> mPermissionManager;
};
// {822bcd11-6432-48be-9e9d-36f7804b7747}
#define NS_POPUPWINDOWMANAGER_CID \
{0x822bcd11, 0x6432, 0x48be, {0x9e, 0x9d, 0x36, 0xf7, 0x80, 0x4b, 0x77, 0x47}}
#endif /* nsPopupWindowManager_h__ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -0,0 +1,3 @@
Cache-Control: no-store
Set-Cookie: mike=beltzer

View file

@ -0,0 +1,3 @@
[DEFAULT]
[browser_test_favicon.js]

View file

@ -0,0 +1,28 @@
// tests third party cookie blocking using a favicon load directly from chrome.
// in this case, the docshell of the channel is chrome, not content; thus
// the cookie should be considered third party.
function test() {
waitForExplicitFinish();
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
Services.obs.addObserver(function (theSubject, theTopic, theData) {
var uri = theSubject.QueryInterface(Components.interfaces.nsIURI);
var domain = uri.host;
if (domain == "example.org") {
ok(true, "foreign favicon cookie was blocked");
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
Services.obs.removeObserver(arguments.callee, "cookie-rejected");
finish();
}
}, "cookie-rejected", false);
// kick off a favicon load
gBrowser.setIcon(gBrowser.selectedTab, "http://example.org/tests/extensions/cookie/test/damonbowling.jpg",
Services.scriptSecurityManager.getSystemPrincipal());
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -0,0 +1,2 @@
Cache-Control: no-store
Set-Cookie: damon=bowling

View file

@ -0,0 +1,15 @@
let { classes: Cc, utils: Cu, interfaces: Ci } = Components;
let cs = Cc["@mozilla.org/cookiemanager;1"]
.getService(Ci.nsICookieManager2);
addMessageListener("getCookieCountAndClear", () => {
let count = 0;
for (let list = cs.enumerator; list.hasMoreElements(); list.getNext())
++count;
cs.removeAll();
sendAsyncMessage("getCookieCountAndClear:return", { count });
});
cs.removeAll();

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://example.com/tests/extensions/cookie/test/file_domain_hierarchy_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
<script type="text/javascript">
document.cookie = "can2=has2";
// send a message to our test document, to say we're done loading
window.parent.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta3=tag3">
<script type="text/javascript">
document.cookie = "can3=has3";
// send a message to our test document, to say we're done loading
window.parent.parent.opener.postMessage("message", "http://mochi.test:8888");
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
<script type="text/javascript">
document.cookie = "can2=has2";
// send a message to our test document, to say we're done loading
window.parent.opener.postMessage("message", "http://mochi.test:8888");
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
</head>
<body>
<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_image_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="http://example.org/tests/extensions/cookie/test/test1.css" />
<link rel="stylesheet" type="text/css" media="all" href="http://example.com/tests/extensions/cookie/test/test2.css" />
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
<script type="text/javascript">
function runTest() {
document.cookie = "can2=has2";
// send a message to our test document, to say we're done loading
window.parent.opener.postMessage("message", "http://mochi.test:8888");
}
</script>
</head>
<body>
<img src="http://example.org/tests/extensions/cookie/test/image1.png" onload="runTest()" />
<img src="http://example.com/tests/extensions/cookie/test/image2.png" onload="runTest()" />
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
function runTest() {
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("f_lf_i msg data img", "http://mochi.test:8888");
}
</script>
</head>
<body onload="window.opener.postMessage('f_lf_i msg data page', 'http://mochi.test:8888');">
<img src="http://example.org/tests/extensions/cookie/test/beltzner.jpg" onload="runTest()" />
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://mochi.test:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://127.0.0.1:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
<script type="text/javascript">
document.cookie = "can=has";
// send a message to our test document, to say we're done loading
window.opener.postMessage("message", "http://mochi.test:8888");
</script>
<body>
<iframe name="frame1" src="http://test2.example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,70 @@
const SCRIPT_URL = SimpleTest.getTestFileURL("file_chromecommon.js");
var gExpectedCookies;
var gExpectedLoads;
var gPopup;
var gScript;
var gLoads = 0;
function setupTest(uri, cookies, loads) {
SimpleTest.waitForExplicitFinish();
var prefSet = new Promise(resolve => {
SpecialPowers.pushPrefEnv({ set: [["network.cookie.cookieBehavior", 1]] }, resolve);
});
gScript = SpecialPowers.loadChromeScript(SCRIPT_URL);
gExpectedCookies = cookies;
gExpectedLoads = loads;
// Listen for MessageEvents.
window.addEventListener("message", messageReceiver, false);
prefSet.then(() => {
// load a window which contains an iframe; each will attempt to set
// cookies from their respective domains.
gPopup = window.open(uri, 'hai', 'width=100,height=100');
});
}
function finishTest() {
gScript.destroy();
SimpleTest.finish();
}
/** Receives MessageEvents to this window. */
// Count and check loads.
function messageReceiver(evt) {
is(evt.data, "message", "message data received from popup");
if (evt.data != "message") {
gPopup.close();
window.removeEventListener("message", messageReceiver, false);
finishTest();
return;
}
// only run the test when all our children are done loading & setting cookies
if (++gLoads == gExpectedLoads) {
gPopup.close();
window.removeEventListener("message", messageReceiver, false);
runTest();
}
}
// runTest() is run by messageReceiver().
// Count and check cookies.
function runTest() {
// set a cookie from a domain of "localhost"
document.cookie = "oh=hai";
gScript.addMessageListener("getCookieCountAndClear:return", ({ count }) => {
is(count, gExpectedCookies, "total number of cookies");
finishTest();
});
gScript.sendAsyncMessage("getCookieCountAndClear");
}

View file

@ -0,0 +1,104 @@
const SCRIPT_URL = SimpleTest.getTestFileURL('file_testloadflags_chromescript.js');
var gExpectedCookies;
var gExpectedHeaders;
var gExpectedLoads;
var gObs;
var gPopup;
var gHeaders = 0;
var gLoads = 0;
// setupTest() is run from 'onload='.
function setupTest(uri, domain, cookies, loads, headers) {
info("setupTest uri: " + uri + " domain: " + domain + " cookies: " + cookies +
" loads: " + loads + " headers: " + headers);
SimpleTest.waitForExplicitFinish();
var prefSet = new Promise(resolve => {
SpecialPowers.pushPrefEnv({ set: [["network.cookie.cookieBehavior", 1]] }, resolve);
});
gExpectedCookies = cookies;
gExpectedLoads = loads;
gExpectedHeaders = headers;
gScript = SpecialPowers.loadChromeScript(SCRIPT_URL);
gScript.addMessageListener("info", ({ str }) => info(str));
gScript.addMessageListener("ok", ({ c, m }) => ok(c, m));
gScript.addMessageListener("observer:gotCookie", ({ cookie, uri }) => {
isnot(cookie.indexOf("oh=hai"), -1,
"cookie 'oh=hai' is in header for " + uri);
++gHeaders;
});
var scriptReady = new Promise(resolve => {
gScript.addMessageListener("init:return", resolve);
gScript.sendAsyncMessage("init", { domain });
});
// Listen for MessageEvents.
window.addEventListener("message", messageReceiver, false);
Promise.all([ prefSet, scriptReady ]).then(() => {
// load a window which contains an iframe; each will attempt to set
// cookies from their respective domains.
gPopup = window.open(uri, 'hai', 'width=100,height=100');
});
}
function finishTest()
{
gScript.addMessageListener("shutdown:return", () => {
gScript.destroy();
SimpleTest.finish();
});
gScript.sendAsyncMessage("shutdown");
}
/** Receives MessageEvents to this window. */
// Count and check loads.
function messageReceiver(evt)
{
ok(evt.data == "f_lf_i msg data img" || evt.data == "f_lf_i msg data page",
"message data received from popup");
if (evt.data == "f_lf_i msg data img") {
info("message data received from popup for image");
}
if (evt.data == "f_lf_i msg data page") {
info("message data received from popup for page");
}
if (evt.data != "f_lf_i msg data img" && evt.data != "f_lf_i msg data page") {
info("got this message but don't know what it is " + evt.data);
gPopup.close();
window.removeEventListener("message", messageReceiver, false);
finishTest();
return;
}
// only run the test when all our children are done loading & setting cookies
if (++gLoads == gExpectedLoads) {
gPopup.close();
window.removeEventListener("message", messageReceiver, false);
runTest();
}
}
// runTest() is run by messageReceiver().
// Check headers, and count and check cookies.
function runTest() {
// set a cookie from a domain of "localhost"
document.cookie = "o=noes";
is(gHeaders, gExpectedHeaders, "number of observed request headers");
gScript.addMessageListener("getCookieCount:return", ({ count }) => {
is(count, gExpectedCookies, "total number of cookies");
finishTest();
});
gScript.sendAsyncMessage("getCookieCount");
}

View file

@ -0,0 +1,112 @@
let { classes: Cc, interfaces: Ci } = Components;
var gObs;
function info(s) {
sendAsyncMessage("info", { str: String(s) });
}
function ok(c, m) {
sendAsyncMessage("ok", { c, m });
}
function is(a, b, m) {
ok(Object.is(a, b), m + " (" + a + " === " + b + ")");
}
// Count headers.
function obs() {
info("adding observer");
this.os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
this.os.addObserver(this, "http-on-modify-request", false);
}
obs.prototype = {
observe(theSubject, theTopic, theData) {
info("theSubject " + theSubject);
info("theTopic " + theTopic);
info("theData " + theData);
var channel = theSubject.QueryInterface(Ci.nsIHttpChannel);
info("channel " + channel);
try {
info("channel.URI " + channel.URI);
info("channel.URI.spec " + channel.URI.spec);
channel.visitRequestHeaders({
visitHeader: function(aHeader, aValue) {
info(aHeader + ": " + aValue);
}});
} catch (err) {
ok(false, "catch error " + err);
}
// Ignore notifications we don't care about (like favicons)
if (channel.URI.spec.indexOf(
"http://example.org/tests/extensions/cookie/test/") == -1) {
info("ignoring this one");
return;
}
sendAsyncMessage("observer:gotCookie",
{ cookie: channel.getRequestHeader("Cookie"),
uri: channel.URI.spec });
},
remove() {
info("removing observer");
this.os.removeObserver(this, "http-on-modify-request");
this.os = null;
}
}
function getCookieCount(cs) {
let count = 0;
let list = cs.enumerator;
while (list.hasMoreElements()) {
let cookie = list.getNext().QueryInterface(Ci.nsICookie);
info("cookie: " + cookie);
info("cookie host " + cookie.host + " path " + cookie.path + " name " + cookie.name +
" value " + cookie.value + " isSecure " + cookie.isSecure + " expires " + cookie.expires);
++count;
}
return count;
}
addMessageListener("init", ({ domain }) => {
let cs = Cc["@mozilla.org/cookiemanager;1"]
.getService(Ci.nsICookieManager2);
info("we are going to remove these cookies");
let count = getCookieCount(cs);
info(count + " cookies");
cs.removeAll();
cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62), {});
is(cs.countCookiesFromHost(domain), 1, "number of cookies for domain " + domain);
gObs = new obs();
sendAsyncMessage("init:return");
});
addMessageListener("getCookieCount", () => {
let cs = Cc["@mozilla.org/cookiemanager;1"]
.getService(Ci.nsICookieManager);
let count = getCookieCount(cs);
cs.removeAll();
sendAsyncMessage("getCookieCount:return", { count });
});
addMessageListener("shutdown", () => {
gObs.remove();
let cs = Cc["@mozilla.org/cookiemanager;1"]
.getService(Ci.nsICookieManager2);
cs.removeAll();
sendAsyncMessage("shutdown:return");
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

View file

@ -0,0 +1,3 @@
Cache-Control: no-store
Set-Cookie: foo=bar

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

View file

@ -0,0 +1,3 @@
Cache-Control: no-store
Set-Cookie: foo2=bar2

View file

@ -0,0 +1,41 @@
[DEFAULT]
support-files =
beltzner.jpg
beltzner.jpg^headers^
damonbowling.jpg
damonbowling.jpg^headers^
file_chromecommon.js
file_domain_hierarchy_inner.html
file_domain_hierarchy_inner_inner.html
file_domain_hierarchy_inner_inner_inner.html
file_domain_inner.html
file_domain_inner_inner.html
file_image_inner.html
file_image_inner_inner.html
file_loadflags_inner.html
file_localhost_inner.html
file_loopback_inner.html
file_subdomain_inner.html
file_testcommon.js
file_testloadflags.js
file_testloadflags_chromescript.js
image1.png
image1.png^headers^
image2.png
image2.png^headers^
test1.css
test1.css^headers^
test2.css
test2.css^headers^
[test_different_domain_in_hierarchy.html]
[test_differentdomain.html]
[test_image.html]
[test_loadflags.html]
[test_same_base_domain.html]
[test_same_base_domain_2.html]
[test_same_base_domain_3.html]
[test_same_base_domain_4.html]
[test_same_base_domain_5.html]
[test_same_base_domain_6.html]
[test_samedomain.html]

View file

@ -0,0 +1,15 @@
# -*- 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/.
XPCSHELL_TESTS_MANIFESTS += [
'unit/xpcshell.ini',
'unit_ipc/xpcshell.ini',
]
MOCHITEST_MANIFESTS += ['mochitest.ini']
BROWSER_CHROME_MANIFESTS += ['browser.ini']

View file

@ -0,0 +1,2 @@

View file

@ -0,0 +1,3 @@
Cache-Control: no-cache
Set-Cookie: css=bar

View file

@ -0,0 +1,2 @@

View file

@ -0,0 +1,3 @@
Cache-Control: no-cache
Set-Cookie: css2=bar2

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test cookie requests from within a window hierarchy of different base domains</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_domain_hierarchy_inner.html', 3, 3)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://example.com/tests/extensions/cookie/test/file_domain_inner.html', 3, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_image_inner.html', 7, 3)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js"></script>
</pre>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<!--
*5 cookies: 1+1 from file_testloadflags.js, 2 from file_loadflags_inner.html + 1 from beltzner.jpg.
*1 load: file_loadflags_inner.html.
*2 headers: 1 for file_loadflags_inner.html + 1 for beltzner.jpg.
-->
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_loadflags_inner.html', 'example.org', 5, 2, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testloadflags.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://test1.example.org/tests/extensions/cookie/test/file_domain_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://test1.example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://mochi.test:8888/tests/extensions/cookie/test/file_localhost_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://sub1.test1.example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://127.0.0.1:8888/tests/extensions/cookie/test/file_loopback_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Cross domain access to properties</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_domain_inner.html', 5, 2)">
<p id="display"></p>
<pre id="test">
<script class="testbody" type="text/javascript" src="file_testcommon.js">
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var Ci = Components.interfaces;
function CookiePromptService() {
}
CookiePromptService.prototype = {
classID: Components.ID("{509b5540-c87c-11dd-ad8b-0800200c9a66}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsICookiePromptService]),
cookieDialog: function(parent, cookie, hostname,
cookiesFromHost, changingCookie,
rememberDecision) {
return 0;
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CookiePromptService]);

View file

@ -0,0 +1,2 @@
component {509b5540-c87c-11dd-ad8b-0800200c9a66} cookieprompt.js
contract @mozilla.org/embedcomp/cookieprompt-service;1 {509b5540-c87c-11dd-ad8b-0800200c9a66}

View file

@ -0,0 +1,570 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
XPCOMUtils.defineLazyServiceGetter(Services, "cookies",
"@mozilla.org/cookieService;1",
"nsICookieService");
XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr",
"@mozilla.org/cookiemanager;1",
"nsICookieManager2");
XPCOMUtils.defineLazyServiceGetter(Services, "etld",
"@mozilla.org/network/effective-tld-service;1",
"nsIEffectiveTLDService");
function do_check_throws(f, result, stack)
{
if (!stack)
stack = Components.stack.caller;
try {
f();
} catch (exc) {
if (exc.result == result)
return;
do_throw("expected result " + result + ", caught " + exc, stack);
}
do_throw("expected result " + result + ", none thrown", stack);
}
// Helper to step a generator function and catch a StopIteration exception.
function do_run_generator(generator)
{
try {
generator.next();
} catch (e) {
if (e != StopIteration)
do_throw("caught exception " + e, Components.stack.caller);
}
}
// Helper to finish a generator function test.
function do_finish_generator_test(generator)
{
do_execute_soon(function() {
generator.close();
do_test_finished();
});
}
function _observer(generator, topic) {
Services.obs.addObserver(this, topic, false);
this.generator = generator;
this.topic = topic;
}
_observer.prototype = {
observe: function (subject, topic, data) {
do_check_eq(this.topic, topic);
Services.obs.removeObserver(this, this.topic);
// Continue executing the generator function.
if (this.generator)
do_run_generator(this.generator);
this.generator = null;
this.topic = null;
}
}
// Close the cookie database. If a generator is supplied, it will be invoked
// once the close is complete.
function do_close_profile(generator) {
// Register an observer for db close.
let obs = new _observer(generator, "cookie-db-closed");
// Close the db.
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
service.observe(null, "profile-before-change", "shutdown-persist");
}
// Load the cookie database. If a generator is supplied, it will be invoked
// once the load is complete.
function do_load_profile(generator) {
// Register an observer for read completion.
let obs = new _observer(generator, "cookie-db-read");
// Load the profile.
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
service.observe(null, "profile-do-change", "");
}
// Set a single session cookie using http and test the cookie count
// against 'expected'
function do_set_single_http_cookie(uri, channel, expected) {
Services.cookies.setCookieStringFromHttp(uri, null, null, "foo=bar", null, channel);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected);
}
// Set four cookies; with & without channel, http and non-http; and test
// the cookie count against 'expected' after each set.
function do_set_cookies(uri, channel, session, expected) {
let suffix = session ? "" : "; max-age=1000";
// without channel
Services.cookies.setCookieString(uri, null, "oh=hai" + suffix, null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[0]);
// with channel
Services.cookies.setCookieString(uri, null, "can=has" + suffix, channel);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[1]);
// without channel, from http
Services.cookies.setCookieStringFromHttp(uri, null, null, "cheez=burger" + suffix, null, null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[2]);
// with channel, from http
Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]);
}
function do_count_enumerator(enumerator) {
let i = 0;
while (enumerator.hasMoreElements()) {
enumerator.getNext();
++i;
}
return i;
}
function do_count_cookies() {
return do_count_enumerator(Services.cookiemgr.enumerator);
}
// Helper object to store cookie data.
function Cookie(name,
value,
host,
path,
expiry,
lastAccessed,
creationTime,
isSession,
isSecure,
isHttpOnly)
{
this.name = name;
this.value = value;
this.host = host;
this.path = path;
this.expiry = expiry;
this.lastAccessed = lastAccessed;
this.creationTime = creationTime;
this.isSession = isSession;
this.isSecure = isSecure;
this.isHttpOnly = isHttpOnly;
let strippedHost = host.charAt(0) == '.' ? host.slice(1) : host;
try {
this.baseDomain = Services.etld.getBaseDomainFromHost(strippedHost);
} catch (e) {
if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS)
this.baseDomain = strippedHost;
}
}
// Object representing a database connection and associated statements. The
// implementation varies depending on schema version.
function CookieDatabaseConnection(file, schema)
{
// Manually generate a cookies.sqlite file with appropriate rows, columns,
// and schema version. If it already exists, just set up our statements.
let exists = file.exists();
this.db = Services.storage.openDatabase(file);
this.schema = schema;
if (!exists)
this.db.schemaVersion = schema;
switch (schema) {
case 1:
{
if (!exists) {
this.db.executeSimpleSQL(
"CREATE TABLE moz_cookies ( \
id INTEGER PRIMARY KEY, \
name TEXT, \
value TEXT, \
host TEXT, \
path TEXT, \
expiry INTEGER, \
isSecure INTEGER, \
isHttpOnly INTEGER)");
}
this.stmtInsert = this.db.createStatement(
"INSERT INTO moz_cookies ( \
id, \
name, \
value, \
host, \
path, \
expiry, \
isSecure, \
isHttpOnly) \
VALUES ( \
:id, \
:name, \
:value, \
:host, \
:path, \
:expiry, \
:isSecure, \
:isHttpOnly)");
this.stmtDelete = this.db.createStatement(
"DELETE FROM moz_cookies WHERE id = :id");
break;
}
case 2:
{
if (!exists) {
this.db.executeSimpleSQL(
"CREATE TABLE moz_cookies ( \
id INTEGER PRIMARY KEY, \
name TEXT, \
value TEXT, \
host TEXT, \
path TEXT, \
expiry INTEGER, \
lastAccessed INTEGER, \
isSecure INTEGER, \
isHttpOnly INTEGER)");
}
this.stmtInsert = this.db.createStatement(
"INSERT OR REPLACE INTO moz_cookies ( \
id, \
name, \
value, \
host, \
path, \
expiry, \
lastAccessed, \
isSecure, \
isHttpOnly) \
VALUES ( \
:id, \
:name, \
:value, \
:host, \
:path, \
:expiry, \
:lastAccessed, \
:isSecure, \
:isHttpOnly)");
this.stmtDelete = this.db.createStatement(
"DELETE FROM moz_cookies WHERE id = :id");
this.stmtUpdate = this.db.createStatement(
"UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id");
break;
}
case 3:
{
if (!exists) {
this.db.executeSimpleSQL(
"CREATE TABLE moz_cookies ( \
id INTEGER PRIMARY KEY, \
baseDomain TEXT, \
name TEXT, \
value TEXT, \
host TEXT, \
path TEXT, \
expiry INTEGER, \
lastAccessed INTEGER, \
isSecure INTEGER, \
isHttpOnly INTEGER)");
this.db.executeSimpleSQL(
"CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)");
}
this.stmtInsert = this.db.createStatement(
"INSERT INTO moz_cookies ( \
id, \
baseDomain, \
name, \
value, \
host, \
path, \
expiry, \
lastAccessed, \
isSecure, \
isHttpOnly) \
VALUES ( \
:id, \
:baseDomain, \
:name, \
:value, \
:host, \
:path, \
:expiry, \
:lastAccessed, \
:isSecure, \
:isHttpOnly)");
this.stmtDelete = this.db.createStatement(
"DELETE FROM moz_cookies WHERE id = :id");
this.stmtUpdate = this.db.createStatement(
"UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id");
break;
}
case 4:
{
if (!exists) {
this.db.executeSimpleSQL(
"CREATE TABLE moz_cookies ( \
id INTEGER PRIMARY KEY, \
baseDomain TEXT, \
name TEXT, \
value TEXT, \
host TEXT, \
path TEXT, \
expiry INTEGER, \
lastAccessed INTEGER, \
creationTime INTEGER, \
isSecure INTEGER, \
isHttpOnly INTEGER \
CONSTRAINT moz_uniqueid UNIQUE (name, host, path))");
this.db.executeSimpleSQL(
"CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)");
this.db.executeSimpleSQL(
"PRAGMA journal_mode = WAL");
}
this.stmtInsert = this.db.createStatement(
"INSERT INTO moz_cookies ( \
baseDomain, \
name, \
value, \
host, \
path, \
expiry, \
lastAccessed, \
creationTime, \
isSecure, \
isHttpOnly) \
VALUES ( \
:baseDomain, \
:name, \
:value, \
:host, \
:path, \
:expiry, \
:lastAccessed, \
:creationTime, \
:isSecure, \
:isHttpOnly)");
this.stmtDelete = this.db.createStatement(
"DELETE FROM moz_cookies \
WHERE name = :name AND host = :host AND path = :path");
this.stmtUpdate = this.db.createStatement(
"UPDATE moz_cookies SET lastAccessed = :lastAccessed \
WHERE name = :name AND host = :host AND path = :path");
break;
}
default:
do_throw("unrecognized schemaVersion!");
}
}
CookieDatabaseConnection.prototype =
{
insertCookie: function(cookie)
{
if (!(cookie instanceof Cookie))
do_throw("not a cookie");
switch (this.schema)
{
case 1:
this.stmtInsert.bindByName("id", cookie.creationTime);
this.stmtInsert.bindByName("name", cookie.name);
this.stmtInsert.bindByName("value", cookie.value);
this.stmtInsert.bindByName("host", cookie.host);
this.stmtInsert.bindByName("path", cookie.path);
this.stmtInsert.bindByName("expiry", cookie.expiry);
this.stmtInsert.bindByName("isSecure", cookie.isSecure);
this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
break;
case 2:
this.stmtInsert.bindByName("id", cookie.creationTime);
this.stmtInsert.bindByName("name", cookie.name);
this.stmtInsert.bindByName("value", cookie.value);
this.stmtInsert.bindByName("host", cookie.host);
this.stmtInsert.bindByName("path", cookie.path);
this.stmtInsert.bindByName("expiry", cookie.expiry);
this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
this.stmtInsert.bindByName("isSecure", cookie.isSecure);
this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
break;
case 3:
this.stmtInsert.bindByName("id", cookie.creationTime);
this.stmtInsert.bindByName("baseDomain", cookie.baseDomain);
this.stmtInsert.bindByName("name", cookie.name);
this.stmtInsert.bindByName("value", cookie.value);
this.stmtInsert.bindByName("host", cookie.host);
this.stmtInsert.bindByName("path", cookie.path);
this.stmtInsert.bindByName("expiry", cookie.expiry);
this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
this.stmtInsert.bindByName("isSecure", cookie.isSecure);
this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
break;
case 4:
this.stmtInsert.bindByName("baseDomain", cookie.baseDomain);
this.stmtInsert.bindByName("name", cookie.name);
this.stmtInsert.bindByName("value", cookie.value);
this.stmtInsert.bindByName("host", cookie.host);
this.stmtInsert.bindByName("path", cookie.path);
this.stmtInsert.bindByName("expiry", cookie.expiry);
this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
this.stmtInsert.bindByName("creationTime", cookie.creationTime);
this.stmtInsert.bindByName("isSecure", cookie.isSecure);
this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
break;
default:
do_throw("unrecognized schemaVersion!");
}
do_execute_stmt(this.stmtInsert);
},
deleteCookie: function(cookie)
{
if (!(cookie instanceof Cookie))
do_throw("not a cookie");
switch (this.db.schemaVersion)
{
case 1:
case 2:
case 3:
this.stmtDelete.bindByName("id", cookie.creationTime);
break;
case 4:
this.stmtDelete.bindByName("name", cookie.name);
this.stmtDelete.bindByName("host", cookie.host);
this.stmtDelete.bindByName("path", cookie.path);
break;
default:
do_throw("unrecognized schemaVersion!");
}
do_execute_stmt(this.stmtDelete);
},
updateCookie: function(cookie)
{
if (!(cookie instanceof Cookie))
do_throw("not a cookie");
switch (this.db.schemaVersion)
{
case 1:
do_throw("can't update a schema 1 cookie!");
case 2:
case 3:
this.stmtUpdate.bindByName("id", cookie.creationTime);
this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed);
break;
case 4:
this.stmtDelete.bindByName("name", cookie.name);
this.stmtDelete.bindByName("host", cookie.host);
this.stmtDelete.bindByName("path", cookie.path);
this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed);
break;
default:
do_throw("unrecognized schemaVersion!");
}
do_execute_stmt(this.stmtUpdate);
},
close: function()
{
this.stmtInsert.finalize();
this.stmtDelete.finalize();
if (this.stmtUpdate)
this.stmtUpdate.finalize();
this.db.close();
this.stmtInsert = null;
this.stmtDelete = null;
this.stmtUpdate = null;
this.db = null;
}
}
function do_get_cookie_file(profile)
{
let file = profile.clone();
file.append("cookies.sqlite");
return file;
}
// Count the cookies from 'host' in a database. If 'host' is null, count all
// cookies.
function do_count_cookies_in_db(connection, host)
{
let select = null;
if (host) {
select = connection.createStatement(
"SELECT COUNT(1) FROM moz_cookies WHERE host = :host");
select.bindByName("host", host);
} else {
select = connection.createStatement(
"SELECT COUNT(1) FROM moz_cookies");
}
select.executeStep();
let result = select.getInt32(0);
select.reset();
select.finalize();
return result;
}
// Execute 'stmt', ensuring that we reset it if it throws.
function do_execute_stmt(stmt)
{
try {
stmt.executeStep();
stmt.reset();
} catch (e) {
stmt.reset();
throw e;
}
}

View file

@ -0,0 +1,248 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var expiry = (Date.now() + 1000) * 1000;
cm.removeAll();
// Allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// test that variants of 'baz.com' get normalized appropriately, but that
// malformed hosts are rejected
cm.add("baz.com", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
do_check_eq(cm.countCookiesFromHost("BAZ.com"), 1);
do_check_eq(cm.countCookiesFromHost(".baz.com"), 1);
do_check_eq(cm.countCookiesFromHost("baz.com."), 0);
do_check_eq(cm.countCookiesFromHost(".baz.com."), 0);
do_check_throws(function() {
cm.countCookiesFromHost("baz.com..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("baz..com");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("..baz.com");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.remove("BAZ.com.", "foo", "/", false, {});
do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
cm.remove("baz.com", "foo", "/", false, {});
do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
// Test that 'baz.com' and 'baz.com.' are treated differently
cm.add("baz.com.", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
do_check_eq(cm.countCookiesFromHost("BAZ.com"), 0);
do_check_eq(cm.countCookiesFromHost(".baz.com"), 0);
do_check_eq(cm.countCookiesFromHost("baz.com."), 1);
do_check_eq(cm.countCookiesFromHost(".baz.com."), 1);
cm.remove("baz.com", "foo", "/", false, {});
do_check_eq(cm.countCookiesFromHost("baz.com."), 1);
cm.remove("baz.com.", "foo", "/", false, {});
do_check_eq(cm.countCookiesFromHost("baz.com."), 0);
// test that domain cookies are illegal for IP addresses, aliases such as
// 'localhost', and eTLD's such as 'co.uk'
cm.add("192.168.0.1", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("192.168.0.1"), 1);
do_check_eq(cm.countCookiesFromHost("192.168.0.1."), 0);
do_check_throws(function() {
cm.countCookiesFromHost(".192.168.0.1");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".192.168.0.1.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.add("localhost", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("localhost"), 1);
do_check_eq(cm.countCookiesFromHost("localhost."), 0);
do_check_throws(function() {
cm.countCookiesFromHost(".localhost");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".localhost.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.add("co.uk", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("co.uk"), 1);
do_check_eq(cm.countCookiesFromHost("co.uk."), 0);
do_check_throws(function() {
cm.countCookiesFromHost(".co.uk");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".co.uk.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that setting an empty or '.' http:// host results in a no-op
var uri = NetUtil.newURI("http://baz.com/");
var emptyuri = NetUtil.newURI("http:///");
var doturi = NetUtil.newURI("http://./");
do_check_eq(uri.asciiHost, "baz.com");
do_check_eq(emptyuri.asciiHost, "");
do_check_eq(doturi.asciiHost, ".");
cs.setCookieString(emptyuri, null, "foo2=bar", null);
do_check_eq(getCookieCount(), 0);
cs.setCookieString(doturi, null, "foo3=bar", null);
do_check_eq(getCookieCount(), 0);
cs.setCookieString(uri, null, "foo=bar", null);
do_check_eq(getCookieCount(), 1);
do_check_eq(cs.getCookieString(uri, null), "foo=bar");
do_check_eq(cs.getCookieString(emptyuri, null), null);
do_check_eq(cs.getCookieString(doturi, null), null);
do_check_eq(cm.countCookiesFromHost(""), 0);
do_check_throws(function() {
cm.countCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
var e = cm.getCookiesFromHost("", {});
do_check_false(e.hasMoreElements());
do_check_throws(function() {
cm.getCookiesFromHost(".", {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.getCookiesFromHost("..", {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
e = cm.getCookiesFromHost("baz.com", {});
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).name, "foo");
do_check_false(e.hasMoreElements());
e = cm.getCookiesFromHost("", {});
do_check_false(e.hasMoreElements());
do_check_throws(function() {
cm.getCookiesFromHost(".", {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.getCookiesFromHost("..", {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that an empty file:// host works
emptyuri = NetUtil.newURI("file:///");
do_check_eq(emptyuri.asciiHost, "");
do_check_eq(NetUtil.newURI("file://./").asciiHost, "");
do_check_eq(NetUtil.newURI("file://foo.bar/").asciiHost, "");
cs.setCookieString(emptyuri, null, "foo2=bar", null);
do_check_eq(getCookieCount(), 1);
cs.setCookieString(emptyuri, null, "foo3=bar; domain=", null);
do_check_eq(getCookieCount(), 2);
cs.setCookieString(emptyuri, null, "foo4=bar; domain=.", null);
do_check_eq(getCookieCount(), 2);
cs.setCookieString(emptyuri, null, "foo5=bar; domain=bar.com", null);
do_check_eq(getCookieCount(), 2);
do_check_eq(cs.getCookieString(emptyuri, null), "foo2=bar; foo3=bar");
do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
do_check_eq(cm.countCookiesFromHost(""), 2);
do_check_throws(function() {
cm.countCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
e = cm.getCookiesFromHost("baz.com", {});
do_check_false(e.hasMoreElements());
e = cm.getCookiesFromHost("", {});
do_check_true(e.hasMoreElements());
e.getNext();
do_check_true(e.hasMoreElements());
e.getNext();
do_check_false(e.hasMoreElements());
do_check_throws(function() {
cm.getCookiesFromHost(".", {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that an empty host to add() or remove() works,
// but a host of '.' doesn't
cm.add("", "/", "foo2", "bar", false, false, true, expiry, {});
do_check_eq(getCookieCount(), 1);
do_check_throws(function() {
cm.add(".", "/", "foo3", "bar", false, false, true, expiry, {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 1);
cm.remove("", "foo2", "/", false, {});
do_check_eq(getCookieCount(), 0);
do_check_throws(function() {
cm.remove(".", "foo3", "/", false, {});
}, Cr.NS_ERROR_ILLEGAL_VALUE);
// test that the 'domain' attribute accepts a leading dot for IP addresses,
// aliases such as 'localhost', and eTLD's such as 'co.uk'; but that the
// resulting cookie is for the exact host only.
testDomainCookie("http://192.168.0.1/", "192.168.0.1");
testDomainCookie("http://localhost/", "localhost");
testDomainCookie("http://co.uk/", "co.uk");
// Test that trailing dots are treated differently for purposes of the
// 'domain' attribute when using setCookieString.
testTrailingDotCookie("http://localhost", "localhost");
testTrailingDotCookie("http://foo.com", "foo.com");
cm.removeAll();
}
function getCookieCount() {
var count = 0;
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var enumerator = cm.enumerator;
while (enumerator.hasMoreElements()) {
if (!(enumerator.getNext() instanceof Ci.nsICookie2))
throw new Error("not a cookie");
++count;
}
return count;
}
function testDomainCookie(uriString, domain) {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
cm.removeAll();
var uri = NetUtil.newURI(uriString);
cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null);
var e = cm.getCookiesFromHost(domain, {});
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
cm.removeAll();
cs.setCookieString(uri, null, "foo=bar; domain=." + domain, null);
e = cm.getCookiesFromHost(domain, {});
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
cm.removeAll();
}
function testTrailingDotCookie(uriString, domain) {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
cm.removeAll();
var uri = NetUtil.newURI(uriString);
cs.setCookieString(uri, null, "foo=bar; domain=" + domain + ".", null);
do_check_eq(cm.countCookiesFromHost(domain), 0);
do_check_eq(cm.countCookiesFromHost(domain + "."), 0);
cm.removeAll();
uri = NetUtil.newURI(uriString + ".");
cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null);
do_check_eq(cm.countCookiesFromHost(domain), 0);
do_check_eq(cm.countCookiesFromHost(domain + "."), 0);
cm.removeAll();
}

View file

@ -0,0 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function run_test() {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var expiry = (Date.now() + 1000) * 1000;
// Test our handling of host names with a single character at the beginning
// followed by a dot.
cm.add("e.mail.com", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("e.mail.com"), 1);
do_check_eq(cs.getCookieString(NetUtil.newURI("http://e.mail.com"), null), "foo=bar");
}

View file

@ -0,0 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function run_test() {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var expiry = (Date.now() + 1000) * 1000;
// Test our handling of host names with a single character consisting only
// of a single character
cm.add("a", "/", "foo", "bar", false, false, true, expiry, {});
do_check_eq(cm.countCookiesFromHost("a"), 1);
do_check_eq(cs.getCookieString(NetUtil.newURI("http://a"), null), "foo=bar");
}

View file

@ -0,0 +1,600 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test the various ways opening a cookie database can fail in an asynchronous
// (i.e. after synchronous initialization) manner, and that the database is
// renamed and recreated under each circumstance. These circumstances are, in no
// particular order:
//
// 1) A write operation failing after the database has been read in.
// 2) Asynchronous read failure due to a corrupt database.
// 3) Synchronous read failure due to a corrupt database, when reading:
// a) a single base domain;
// b) the entire database.
// 4) Asynchronous read failure, followed by another failure during INSERT but
// before the database closes for rebuilding. (The additional error should be
// ignored.)
// 5) Asynchronous read failure, followed by an INSERT failure during rebuild.
// This should result in an abort of the database rebuild; the partially-
// built database should be moved to 'cookies.sqlite.bak-rebuild'.
var test_generator = do_run_test();
function run_test() {
do_test_pending();
do_run_generator(test_generator);
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
this.profile = do_get_profile();
// Allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// Get the cookie file and the backup file.
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
// Create a cookie object for testing.
this.now = Date.now() * 1000;
this.futureExpiry = Math.round(this.now / 1e6 + 1000);
this.cookie = new Cookie("oh", "hai", "bar.com", "/", this.futureExpiry,
this.now, this.now, false, false, false);
this.sub_generator = run_test_1(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_2(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_4(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_5(test_generator);
sub_generator.next();
yield;
finish_test();
return;
}
function do_get_backup_file(profile)
{
let file = profile.clone();
file.append("cookies.sqlite.bak");
return file;
}
function do_get_rebuild_backup_file(profile)
{
let file = profile.clone();
file.append("cookies.sqlite.bak-rebuild");
return file;
}
function do_corrupt_db(file)
{
// Sanity check: the database size should be larger than 450k, since we've
// written about 460k of data. If it's not, let's make it obvious now.
let size = file.fileSize;
do_check_true(size > 450e3);
// Corrupt the database by writing bad data to the end of the file. We
// assume that the important metadata -- table structure etc -- is stored
// elsewhere, and that doing this will not cause synchronous failure when
// initializing the database connection. This is totally empirical --
// overwriting between 1k and 100k of live data seems to work. (Note that the
// database file will be larger than the actual content requires, since the
// cookie service uses a large growth increment. So we calculate the offset
// based on the expected size of the content, not just the file size.)
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, 2, -1, 0);
let sstream = ostream.QueryInterface(Ci.nsISeekableStream);
let n = size - 450e3 + 20e3;
sstream.seek(Ci.nsISeekableStream.NS_SEEK_SET, size - n);
for (let i = 0; i < n; ++i) {
ostream.write("a", 1);
}
ostream.flush();
ostream.close();
do_check_eq(file.clone().fileSize, size);
return size;
}
function run_test_1(generator)
{
// Load the profile and populate it.
let uri = NetUtil.newURI("http://foo.com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Open a database connection now, before we load the profile and begin
// asynchronous write operations. In order to tell when the async delete
// statement has completed, we do something tricky: open a schema 2 connection
// and add a cookie with null baseDomain. We can then wait until we see it
// deleted in the new database.
let db2 = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
db2.db.executeSimpleSQL("INSERT INTO moz_cookies (baseDomain) VALUES (NULL)");
db2.close();
let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4);
do_check_eq(do_count_cookies_in_db(db.db), 2);
// Load the profile, and wait for async read completion...
do_load_profile(sub_generator);
yield;
// ... and the DELETE statement to finish.
while (do_count_cookies_in_db(db.db) == 2) {
do_execute_soon(function() {
do_run_generator(sub_generator);
});
yield;
}
do_check_eq(do_count_cookies_in_db(db.db), 1);
// Insert a row.
db.insertCookie(cookie);
db.close();
// Attempt to insert a cookie with the same (name, host, path) triplet.
Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo",
cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry, {});
// Check that the cookie service accepted the new cookie.
do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
// Wait for the cookie service to rename the old database and rebuild.
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// At this point, the cookies should still be in memory.
do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
do_check_eq(do_count_cookies(), 2);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the original database was renamed, and that it contains the
// original cookie.
do_check_true(do_get_backup_file(profile).exists());
let backupdb = Services.storage.openDatabase(do_get_backup_file(profile));
do_check_eq(do_count_cookies_in_db(backupdb, "foo.com"), 1);
backupdb.close();
// Load the profile, and check that it contains the new cookie.
do_load_profile();
do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1);
let enumerator = Services.cookiemgr.getCookiesFromHost(cookie.host, {});
do_check_true(enumerator.hasMoreElements());
let dbcookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
do_check_eq(dbcookie.value, "hallo");
do_check_false(enumerator.hasMoreElements());
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
do_get_cookie_file(profile).remove(false);
do_get_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_run_generator(generator);
}
function run_test_2(generator)
{
// Load the profile and populate it.
do_load_profile();
for (let i = 0; i < 3000; ++i) {
let uri = NetUtil.newURI("http://" + i + ".com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
}
// Close the profile.
do_close_profile(sub_generator);
yield;
// Corrupt the database file.
let size = do_corrupt_db(do_get_cookie_file(profile));
// Load the profile.
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in the first cookie. This will cause it to go into the
// cookie table, whereupon it will be written out during database rebuild.
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
// Wait for the asynchronous read to choke, at which point the backup file
// will be created and the database rebuilt.
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// At this point, the cookies should still be in memory.
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
do_check_eq(do_count_cookies(), 1);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the original database was renamed.
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
let db = Services.storage.openDatabase(do_get_cookie_file(profile));
do_check_eq(do_count_cookies_in_db(db, "0.com"), 1);
db.close();
// Load the profile, and check that it contains the new cookie.
do_load_profile();
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
do_check_eq(do_count_cookies(), 1);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
do_get_cookie_file(profile).remove(false);
do_get_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_run_generator(generator);
}
function run_test_3(generator)
{
// Set the maximum cookies per base domain limit to a large value, so that
// corrupting the database is easier.
Services.prefs.setIntPref("network.cookie.maxPerHost", 3000);
// Load the profile and populate it.
do_load_profile();
for (let i = 0; i < 10; ++i) {
let uri = NetUtil.newURI("http://hither.com/");
Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000",
null);
}
for (let i = 10; i < 3000; ++i) {
let uri = NetUtil.newURI("http://haithur.com/");
Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000",
null);
}
// Close the profile.
do_close_profile(sub_generator);
yield;
// Corrupt the database file.
let size = do_corrupt_db(do_get_cookie_file(profile));
// Load the profile.
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in the cookies for our two domains. The first should
// succeed, but the second should fail midway through, resulting in none of
// those cookies being present.
do_check_eq(Services.cookiemgr.countCookiesFromHost("hither.com"), 10);
do_check_eq(Services.cookiemgr.countCookiesFromHost("haithur.com"), 0);
// Wait for the backup file to be created and the database rebuilt.
do_check_false(do_get_backup_file(profile).exists());
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// Close the profile.
do_close_profile(sub_generator);
yield;
let db = Services.storage.openDatabase(do_get_cookie_file(profile));
do_check_eq(do_count_cookies_in_db(db, "hither.com"), 10);
do_check_eq(do_count_cookies_in_db(db), 10);
db.close();
// Check that the original database was renamed.
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
// Rename it back, and try loading the entire database synchronously.
do_get_backup_file(profile).moveTo(null, "cookies.sqlite");
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in everything.
do_check_eq(do_count_cookies(), 0);
// Wait for the backup file to be created and the database rebuilt.
do_check_false(do_get_backup_file(profile).exists());
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// Close the profile.
do_close_profile(sub_generator);
yield;
db = Services.storage.openDatabase(do_get_cookie_file(profile));
do_check_eq(do_count_cookies_in_db(db), 0);
db.close();
// Check that the original database was renamed.
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
// Clean up.
do_get_cookie_file(profile).remove(false);
do_get_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_run_generator(generator);
}
function run_test_4(generator)
{
// Load the profile and populate it.
do_load_profile();
for (let i = 0; i < 3000; ++i) {
let uri = NetUtil.newURI("http://" + i + ".com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
}
// Close the profile.
do_close_profile(sub_generator);
yield;
// Corrupt the database file.
let size = do_corrupt_db(do_get_cookie_file(profile));
// Load the profile.
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in the first cookie. This will cause it to go into the
// cookie table, whereupon it will be written out during database rebuild.
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
// Queue up an INSERT for the same base domain. This should also go into
// memory and be written out during database rebuild.
let uri = NetUtil.newURI("http://0.com/");
Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null);
// Wait for the asynchronous read to choke and the insert to fail shortly
// thereafter, at which point the backup file will be created and the database
// rebuilt.
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the original database was renamed.
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
let db = Services.storage.openDatabase(do_get_cookie_file(profile));
do_check_eq(do_count_cookies_in_db(db, "0.com"), 2);
db.close();
// Load the profile, and check that it contains the new cookie.
do_load_profile();
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2);
do_check_eq(do_count_cookies(), 2);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
do_get_cookie_file(profile).remove(false);
do_get_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_run_generator(generator);
}
function run_test_4(generator)
{
// Load the profile and populate it.
do_load_profile();
for (let i = 0; i < 3000; ++i) {
let uri = NetUtil.newURI("http://" + i + ".com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
}
// Close the profile.
do_close_profile(sub_generator);
yield;
// Corrupt the database file.
let size = do_corrupt_db(do_get_cookie_file(profile));
// Load the profile.
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in the first cookie. This will cause it to go into the
// cookie table, whereupon it will be written out during database rebuild.
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
// Queue up an INSERT for the same base domain. This should also go into
// memory and be written out during database rebuild.
let uri = NetUtil.newURI("http://0.com/");
Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null);
// Wait for the asynchronous read to choke and the insert to fail shortly
// thereafter, at which point the backup file will be created and the database
// rebuilt.
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
do_execute_soon(function() { do_run_generator(sub_generator); });
yield;
// At this point, the cookies should still be in memory.
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2);
do_check_eq(do_count_cookies(), 2);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the original database was renamed.
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
let db = Services.storage.openDatabase(do_get_cookie_file(profile));
do_check_eq(do_count_cookies_in_db(db, "0.com"), 2);
db.close();
// Load the profile, and check that it contains the new cookie.
do_load_profile();
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2);
do_check_eq(do_count_cookies(), 2);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
do_get_cookie_file(profile).remove(false);
do_get_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_run_generator(generator);
}
function run_test_5(generator)
{
// Load the profile and populate it.
do_load_profile();
let uri = NetUtil.newURI("http://bar.com/");
Services.cookies.setCookieString(uri, null, "oh=hai; path=/; max-age=1000",
null);
for (let i = 0; i < 3000; ++i) {
let uri = NetUtil.newURI("http://" + i + ".com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
}
// Close the profile.
do_close_profile(sub_generator);
yield;
// Corrupt the database file.
let size = do_corrupt_db(do_get_cookie_file(profile));
// Load the profile.
do_load_profile();
// At this point, the database connection should be open. Ensure that it
// succeeded.
do_check_false(do_get_backup_file(profile).exists());
// Synchronously read in the first two cookies. This will cause them to go
// into the cookie table, whereupon it will be written out during database
// rebuild.
do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
// Wait for the asynchronous read to choke, at which point the backup file
// will be created and a new connection opened.
new _observer(sub_generator, "cookie-db-rebuilding");
yield;
// At this point, the cookies should still be in memory. (Note that these
// calls are re-entrant into the cookie service, but it's OK!)
do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
do_check_eq(do_count_cookies(), 2);
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
do_check_false(do_get_rebuild_backup_file(profile).exists());
// Open a database connection, and write a row that will trigger a constraint
// violation.
let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4);
db.insertCookie(cookie);
do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1);
do_check_eq(do_count_cookies_in_db(db.db), 1);
db.close();
// Wait for the rebuild to bail and the database to be closed.
new _observer(sub_generator, "cookie-db-closed");
yield;
// Check that the original backup and the database itself are gone.
do_check_true(do_get_rebuild_backup_file(profile).exists());
do_check_true(do_get_backup_file(profile).exists());
do_check_eq(do_get_backup_file(profile).fileSize, size);
do_check_false(do_get_cookie_file(profile).exists());
// Check that the rebuild backup has the original bar.com cookie, and possibly
// a 0.com cookie depending on whether it got written out first or second.
db = new CookieDatabaseConnection(do_get_rebuild_backup_file(profile), 4);
do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1);
let count = do_count_cookies_in_db(db.db);
do_check_true(count == 1 ||
count == 2 && do_count_cookies_in_db(db.db, "0.com") == 1);
db.close();
do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
do_check_eq(do_count_cookies(), 2);
// Close the profile. We do not need to wait for completion, because the
// database has already been closed.
do_close_profile();
// Clean up.
do_get_backup_file(profile).remove(false);
do_get_rebuild_backup_file(profile).remove(false);
do_check_false(do_get_cookie_file(profile).exists());
do_check_false(do_get_backup_file(profile).exists());
do_check_false(do_get_rebuild_backup_file(profile).exists());
do_run_generator(generator);
}

View file

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// test for cookie persistence across sessions, for the cases:
// 1) network.cookie.lifetimePolicy = 0 (expire naturally)
// 2) network.cookie.lifetimePolicy = 2 (expire at end of session)
var test_generator = do_run_test();
function run_test() {
do_test_pending();
test_generator.next();
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
// Create URIs and channels pointing to foo.com and bar.com.
// We will use these to put foo.com into first and third party contexts.
var spec1 = "http://foo.com/foo.html";
var spec2 = "http://bar.com/bar.html";
var uri1 = NetUtil.newURI(spec1);
var uri2 = NetUtil.newURI(spec2);
var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true});
var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true});
// Force the channel URI to be used when determining the originating URI of
// the channel.
var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
var httpchannel2 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
httpchannel1.forceAllowThirdPartyCookie = true;
httpchannel2.forceAllowThirdPartyCookie = true;
// test with cookies enabled, and third party cookies persistent.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", false);
do_set_cookies(uri1, channel1, false, [1, 2, 3, 4]);
do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]);
// fake a profile change
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
// Again, but don't wait for the async close to complete. This should always
// work, since we blocked on close above and haven't kicked off any writes
// since then.
do_close_profile();
do_load_profile();
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
// test with cookies set to session-only
Services.prefs.setIntPref("network.cookie.lifetimePolicy", 2);
Services.cookies.removeAll();
do_set_cookies(uri1, channel1, false, [1, 2, 3, 4]);
do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]);
// fake a profile change
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
finish_test();
}

View file

@ -0,0 +1,115 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test private browsing mode.
var test_generator = do_run_test();
function run_test() {
do_test_pending();
do_run_generator(test_generator);
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function make_channel(url) {
return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
.QueryInterface(Ci.nsIHttpChannel);
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
// Test with cookies enabled.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// Create URIs pointing to foo.com and bar.com.
let uri1 = NetUtil.newURI("http://foo.com/foo.html");
let uri2 = NetUtil.newURI("http://bar.com/bar.html");
// Set a cookie for host 1.
Services.cookies.setCookieString(uri1, null, "oh=hai; max-age=1000", null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1);
// Enter private browsing mode, set a cookie for host 2, and check the counts.
var chan1 = make_channel(uri1.spec);
chan1.QueryInterface(Ci.nsIPrivateBrowsingChannel);
chan1.setPrivate(true);
var chan2 = make_channel(uri2.spec);
chan2.QueryInterface(Ci.nsIPrivateBrowsingChannel);
chan2.setPrivate(true);
Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2);
do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai");
// Remove cookies and check counts.
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null);
Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai");
// Leave private browsing mode and check counts.
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0);
// Fake a profile change.
do_close_profile(test_generator);
yield;
do_load_profile();
// Check that the right cookie persisted.
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0);
// Enter private browsing mode, set a cookie for host 2, and check the counts.
do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null);
Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai");
// Fake a profile change.
do_close_profile(test_generator);
yield;
do_load_profile();
// We're still in private browsing mode, but should have a new session.
// Check counts.
do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null);
// Leave private browsing mode and check counts.
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0);
// Enter private browsing mode.
// Fake a profile change, but wait for async read completion.
do_close_profile(test_generator);
yield;
do_load_profile(test_generator);
yield;
// We're still in private browsing mode, but should have a new session.
// Check counts.
do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null);
do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null);
// Leave private browsing mode and check counts.
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0);
finish_test();
}

View file

@ -0,0 +1,92 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that the cookie APIs behave sanely after 'profile-before-change'.
var test_generator = do_run_test();
function run_test() {
do_test_pending();
test_generator.next();
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
// Allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// Start the cookieservice.
Services.cookies;
// Set a cookie.
let uri = NetUtil.newURI("http://foo.com");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
let enumerator = Services.cookiemgr.enumerator;
do_check_true(enumerator.hasMoreElements());
let cookie = enumerator.getNext();
do_check_false(enumerator.hasMoreElements());
// Fire 'profile-before-change'.
do_close_profile();
// Check that the APIs behave appropriately.
do_check_eq(Services.cookies.getCookieString(uri, null), null);
do_check_eq(Services.cookies.getCookieStringFromHttp(uri, null, null), null);
Services.cookies.setCookieString(uri, null, "oh2=hai", null);
Services.cookies.setCookieStringFromHttp(uri, null, null, "oh3=hai", null, null);
do_check_eq(Services.cookies.getCookieString(uri, null), null);
do_check_throws(function() {
Services.cookiemgr.removeAll();
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookiemgr.enumerator;
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookiemgr.add("foo.com", "", "oh4", "hai", false, false, false, 0, {});
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookiemgr.remove("foo.com", "", "oh4", false, {});
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
let file = profile.clone();
file.append("cookies.txt");
Services.cookiemgr.importCookies(file);
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookiemgr.cookieExists(cookie);
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookies.countCookiesFromHost("foo.com");
}, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_throws(function() {
Services.cookies.getCookiesFromHost("foo.com", {});
}, Cr.NS_ERROR_NOT_AVAILABLE);
// Wait for the database to finish closing.
new _observer(test_generator, "cookie-db-closed");
yield;
// Load the profile and check that the API is available.
do_load_profile();
do_check_true(Services.cookiemgr.cookieExists(cookie));
finish_test();
}

View file

@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// test cookie database asynchronous read operation.
var test_generator = do_run_test();
var CMAX = 1000; // # of cookies to create
function run_test() {
do_test_pending();
test_generator.next();
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
// Allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// Start the cookieservice, to force creation of a database.
Services.cookies;
// Open a database connection now, after synchronous initialization has
// completed. We may not be able to open one later once asynchronous writing
// begins.
do_check_true(do_get_cookie_file(profile).exists());
let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4);
for (let i = 0; i < CMAX; ++i) {
let uri = NetUtil.newURI("http://" + i + ".com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
}
do_check_eq(do_count_cookies(), CMAX);
// Wait until all CMAX cookies have been written out to the database.
while (do_count_cookies_in_db(db.db) < CMAX) {
do_execute_soon(function() {
do_run_generator(test_generator);
});
yield;
}
// Check the WAL file size. We set it to 16 pages of 32k, which means it
// should be around 500k.
let file = db.db.databaseFile;
do_check_true(file.exists());
do_check_true(file.fileSize < 1e6);
db.close();
// fake a profile change
do_close_profile(test_generator);
yield;
do_load_profile();
// test a few random cookies
do_check_eq(Services.cookiemgr.countCookiesFromHost("999.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("abc.com"), 0);
do_check_eq(Services.cookiemgr.countCookiesFromHost("100.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("400.com"), 1);
do_check_eq(Services.cookiemgr.countCookiesFromHost("xyz.com"), 0);
// force synchronous load of everything
do_check_eq(do_count_cookies(), CMAX);
// check that everything's precisely correct
for (let i = 0; i < CMAX; ++i) {
let host = i.toString() + ".com";
do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1);
}
// reload again, to make sure the additions were written correctly
do_close_profile(test_generator);
yield;
do_load_profile();
// remove some of the cookies, in both reverse and forward order
for (let i = 100; i-- > 0; ) {
let host = i.toString() + ".com";
Services.cookiemgr.remove(host, "oh", "/", false, {});
}
for (let i = CMAX - 100; i < CMAX; ++i) {
let host = i.toString() + ".com";
Services.cookiemgr.remove(host, "oh", "/", false, {});
}
// check the count
do_check_eq(do_count_cookies(), CMAX - 200);
// reload again, to make sure the removals were written correctly
do_close_profile(test_generator);
yield;
do_load_profile();
// check the count
do_check_eq(do_count_cookies(), CMAX - 200);
// reload again, but wait for async read completion
do_close_profile(test_generator);
yield;
do_load_profile(test_generator);
yield;
// check that everything's precisely correct
do_check_eq(do_count_cookies(), CMAX - 200);
for (let i = 100; i < CMAX - 100; ++i) {
let host = i.toString() + ".com";
do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1);
}
finish_test();
}

View file

@ -0,0 +1,286 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test the various ways opening a cookie database can fail in a synchronous
// (i.e. immediate) manner, and that the database is renamed and recreated
// under each circumstance. These circumstances are, in no particular order:
//
// 1) A corrupt database, such that opening the connection fails.
// 2) The 'moz_cookies' table doesn't exist.
// 3) Not all of the expected columns exist, and statement creation fails when:
// a) The schema version is larger than the current version.
// b) The schema version is less than or equal to the current version.
// 4) Migration fails. This will have different modes depending on the initial
// version:
// a) Schema 1: the 'lastAccessed' column already exists.
// b) Schema 2: the 'baseDomain' column already exists; or 'baseDomain'
// cannot be computed for a particular host.
// c) Schema 3: the 'creationTime' column already exists; or the
// 'moz_uniqueid' index already exists.
var COOKIE_DATABASE_SCHEMA_CURRENT = 7;
var test_generator = do_run_test();
function run_test() {
do_test_pending();
do_run_generator(test_generator);
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
this.profile = do_get_profile();
// Allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// Get the cookie file and the backup file.
this.cookieFile = profile.clone();
cookieFile.append("cookies.sqlite");
this.backupFile = profile.clone();
backupFile.append("cookies.sqlite.bak");
do_check_false(cookieFile.exists());
do_check_false(backupFile.exists());
// Create a cookie object for testing.
this.now = Date.now() * 1000;
this.futureExpiry = Math.round(this.now / 1e6 + 1000);
this.cookie = new Cookie("oh", "hai", "bar.com", "/", this.futureExpiry,
this.now, this.now, false, false, false);
this.sub_generator = run_test_1(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_2(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator, 99);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator, COOKIE_DATABASE_SCHEMA_CURRENT);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator, 4);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator, 3);
sub_generator.next();
yield;
this.sub_generator = run_test_4_exists(test_generator, 1,
"ALTER TABLE moz_cookies ADD lastAccessed INTEGER");
sub_generator.next();
yield;
this.sub_generator = run_test_4_exists(test_generator, 2,
"ALTER TABLE moz_cookies ADD baseDomain TEXT");
sub_generator.next();
yield;
this.sub_generator = run_test_4_baseDomain(test_generator);
sub_generator.next();
yield;
this.sub_generator = run_test_4_exists(test_generator, 3,
"ALTER TABLE moz_cookies ADD creationTime INTEGER");
sub_generator.next();
yield;
this.sub_generator = run_test_4_exists(test_generator, 3,
"CREATE UNIQUE INDEX moz_uniqueid ON moz_cookies (name, host, path)");
sub_generator.next();
yield;
finish_test();
return;
}
const garbage = "hello thar!";
function create_garbage_file(file)
{
// Create an empty database file.
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, -1);
do_check_true(file.exists());
do_check_eq(file.fileSize, 0);
// Write some garbage to it.
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, 0);
ostream.write(garbage, garbage.length);
ostream.flush();
ostream.close();
file = file.clone(); // Windows maintains a stat cache. It's lame.
do_check_eq(file.fileSize, garbage.length);
}
function check_garbage_file(file)
{
do_check_true(file.exists());
do_check_eq(file.fileSize, garbage.length);
file.remove(false);
do_check_false(file.exists());
}
function run_test_1(generator)
{
// Create a garbage database file.
create_garbage_file(cookieFile);
// Load the profile and populate it.
let uri = NetUtil.newURI("http://foo.com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
// Fake a profile change.
do_close_profile(sub_generator);
yield;
do_load_profile();
// Check that the new database contains the cookie, and the old file was
// renamed.
do_check_eq(do_count_cookies(), 1);
check_garbage_file(backupFile);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
cookieFile.remove(false);
do_check_false(cookieFile.exists());
do_run_generator(generator);
}
function run_test_2(generator)
{
// Load the profile and populate it.
do_load_profile();
let uri = NetUtil.newURI("http://foo.com/");
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
// Fake a profile change.
do_close_profile(sub_generator);
yield;
// Drop the table.
let db = Services.storage.openDatabase(cookieFile);
db.executeSimpleSQL("DROP TABLE moz_cookies");
db.close();
// Load the profile and check that the table is recreated in-place.
do_load_profile();
do_check_eq(do_count_cookies(), 0);
do_check_false(backupFile.exists());
// Close the profile.
do_close_profile(sub_generator);
yield;
// Clean up.
cookieFile.remove(false);
do_check_false(cookieFile.exists());
do_run_generator(generator);
}
function run_test_3(generator, schema)
{
// Manually create a schema 2 database, populate it, and set the schema
// version to the desired number.
let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
schema2db.insertCookie(cookie);
schema2db.db.schemaVersion = schema;
schema2db.close();
// Load the profile and check that the column existence test fails.
do_load_profile();
do_check_eq(do_count_cookies(), 0);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the schema version has been reset.
let db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close();
// Clean up.
cookieFile.remove(false);
do_check_false(cookieFile.exists());
do_run_generator(generator);
}
function run_test_4_exists(generator, schema, stmt)
{
// Manually create a database, populate it, and add the desired column.
let db = new CookieDatabaseConnection(do_get_cookie_file(profile), schema);
db.insertCookie(cookie);
db.db.executeSimpleSQL(stmt);
db.close();
// Load the profile and check that migration fails.
do_load_profile();
do_check_eq(do_count_cookies(), 0);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the schema version has been reset and the backup file exists.
db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close();
do_check_true(backupFile.exists());
// Clean up.
cookieFile.remove(false);
backupFile.remove(false);
do_check_false(cookieFile.exists());
do_check_false(backupFile.exists());
do_run_generator(generator);
}
function run_test_4_baseDomain(generator)
{
// Manually create a database and populate it with a bad host.
let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
let badCookie = new Cookie("oh", "hai", ".", "/", this.futureExpiry, this.now,
this.now, false, false, false);
db.insertCookie(badCookie);
db.close();
// Load the profile and check that migration fails.
do_load_profile();
do_check_eq(do_count_cookies(), 0);
// Close the profile.
do_close_profile(sub_generator);
yield;
// Check that the schema version has been reset and the backup file exists.
db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close();
do_check_true(backupFile.exists());
// Clean up.
cookieFile.remove(false);
backupFile.remove(false);
do_check_false(cookieFile.exists());
do_check_false(backupFile.exists());
do_run_generator(generator);
}

View file

@ -0,0 +1,147 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// test third party cookie blocking, for the cases:
// 1) with null channel
// 2) with channel, but with no docshell parent
function run_test() {
// Create URIs and channels pointing to foo.com and bar.com.
// We will use these to put foo.com into first and third party contexts.
var spec1 = "http://foo.com/foo.html";
var spec2 = "http://bar.com/bar.html";
var uri1 = NetUtil.newURI(spec1);
var uri2 = NetUtil.newURI(spec2);
var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true});
var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true});
// test with cookies enabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
do_set_cookies(uri1, channel1, true, [1, 2, 3, 4]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [1, 2, 3, 4]);
Services.cookies.removeAll();
// test with third party cookies blocked
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
do_set_cookies(uri1, channel1, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
// Force the channel URI to be used when determining the originating URI of
// the channel.
var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal);
httpchannel1.forceAllowThirdPartyCookie = true;
httpchannel2.forceAllowThirdPartyCookie = true;
// test with cookies enabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
do_set_cookies(uri1, channel1, true, [1, 2, 3, 4]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [1, 2, 3, 4]);
Services.cookies.removeAll();
// test with third party cookies blocked
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
// test with third party cookies limited
Services.prefs.setIntPref("network.cookie.cookieBehavior", 3);
do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri1, channel1, 1);
do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie blocking with cookies enabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
var kPermissionType = "cookie";
var ALLOW_FIRST_PARTY_ONLY = 9;
// ALLOW_FIRST_PARTY_ONLY overrides
Services.perms.add(uri1, kPermissionType, ALLOW_FIRST_PARTY_ONLY);
do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie blocking with 3rd party cookies disabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
// No preference has been set for uri2, but it should act as if
// ALLOW_FIRST_PARTY_ONLY has been set
do_set_cookies(uri2, channel2, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie blocking with 3rd party cookies limited
Services.prefs.setIntPref("network.cookie.cookieBehavior", 3);
do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
// No preference has been set for uri2, but it should act as if
// LIMIT_THIRD_PARTY has been set
do_set_cookies(uri2, channel2, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri2, channel2, 1);
do_set_cookies(uri2, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri1, channel1, 1);
do_set_cookies(uri1, channel2, true, [1, 1, 1, 1]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie limiting with cookies enabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
var kPermissionType = "cookie";
var LIMIT_THIRD_PARTY = 10;
// LIMIT_THIRD_PARTY overrides
Services.perms.add(uri1, kPermissionType, LIMIT_THIRD_PARTY);
do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri1, channel1, 1);
do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie limiting with 3rd party cookies disabled
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
// No preference has been set for uri2, but it should act as if
// ALLOW_FIRST_PARTY_ONLY has been set
do_set_cookies(uri2, channel2, true, [0, 1, 1, 2]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri1, channel1, 1);
do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
// Test per-site 3rd party cookie limiting with 3rd party cookies limited
Services.prefs.setIntPref("network.cookie.cookieBehavior", 3);
do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
// No preference has been set for uri2, but it should act as if
// LIMIT_THIRD_PARTY has been set
do_set_cookies(uri2, channel2, true, [0, 1, 2, 3]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri2, channel2, 1);
do_set_cookies(uri2, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
Services.cookies.removeAll();
do_set_single_http_cookie(uri1, channel1, 1);
do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]);
Services.cookies.removeAll();
}

View file

@ -0,0 +1,69 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// test third party persistence across sessions, for the cases:
// 1) network.cookie.thirdparty.sessionOnly = false
// 2) network.cookie.thirdparty.sessionOnly = true
var test_generator = do_run_test();
function run_test() {
do_test_pending();
test_generator.next();
}
function finish_test() {
do_execute_soon(function() {
test_generator.close();
do_test_finished();
});
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
// Create URIs and channels pointing to foo.com and bar.com.
// We will use these to put foo.com into first and third party contexts.
var spec1 = "http://foo.com/foo.html";
var spec2 = "http://bar.com/bar.html";
var uri1 = NetUtil.newURI(spec1);
var uri2 = NetUtil.newURI(spec2);
var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true});
var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true});
// Force the channel URI to be used when determining the originating URI of
// the channel.
var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal);
httpchannel1.forceAllowThirdPartyCookie = true;
httpchannel2.forceAllowThirdPartyCookie = true;
// test with cookies enabled, and third party cookies persistent.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", false);
do_set_cookies(uri1, channel2, false, [1, 2, 3, 4]);
do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]);
// fake a profile change
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
// test with third party cookies for session only.
Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", true);
Services.cookies.removeAll();
do_set_cookies(uri1, channel2, false, [1, 2, 3, 4]);
do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]);
// fake a profile change
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
finish_test();
}

View file

@ -0,0 +1,153 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that domain eviction occurs when the cookies per base domain limit is
// reached, and that expired cookies are evicted before live cookies.
var test_generator = do_run_test();
function run_test()
{
do_test_pending();
do_run_generator(test_generator);
}
function continue_test()
{
do_run_generator(test_generator);
}
function do_run_test()
{
// Set the base domain limit to 50 so we have a known value.
Services.prefs.setIntPref("network.cookie.maxPerHost", 50);
let futureExpiry = Math.floor(Date.now() / 1000 + 1000);
// test eviction under the 50 cookies per base domain limit. this means
// that cookies for foo.com and bar.foo.com should count toward this limit,
// while cookies for baz.com should not. there are several tests we perform
// to make sure the base domain logic is working correctly.
// 1) simplest case: set 100 cookies for "foo.bar" and make sure 50 survive.
setCookies("foo.bar", 100, futureExpiry);
do_check_eq(countCookies("foo.bar", "foo.bar"), 50);
// 2) set cookies for different subdomains of "foo.baz", and an unrelated
// domain, and make sure all 50 within the "foo.baz" base domain are counted.
setCookies("foo.baz", 10, futureExpiry);
setCookies(".foo.baz", 10, futureExpiry);
setCookies("bar.foo.baz", 10, futureExpiry);
setCookies("baz.bar.foo.baz", 10, futureExpiry);
setCookies("unrelated.domain", 50, futureExpiry);
do_check_eq(countCookies("foo.baz", "baz.bar.foo.baz"), 40);
setCookies("foo.baz", 20, futureExpiry);
do_check_eq(countCookies("foo.baz", "baz.bar.foo.baz"), 50);
// 3) ensure cookies are evicted by order of lastAccessed time, if the
// limit on cookies per base domain is reached.
setCookies("horse.radish", 10, futureExpiry);
// Wait a while, to make sure the first batch of cookies is older than
// the second (timer resolution varies on different platforms).
do_timeout(100, continue_test);
yield;
setCookies("tasty.horse.radish", 50, futureExpiry);
do_check_eq(countCookies("horse.radish", "horse.radish"), 50);
let enumerator = Services.cookiemgr.enumerator;
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.host == "horse.radish")
do_throw("cookies not evicted by lastAccessed order");
}
// Test that expired cookies for a domain are evicted before live ones.
let shortExpiry = Math.floor(Date.now() / 1000 + 2);
setCookies("captchart.com", 49, futureExpiry);
Services.cookiemgr.add("captchart.com", "", "test100", "eviction",
false, false, false, shortExpiry, {});
do_timeout(2100, continue_test);
yield;
do_check_eq(countCookies("captchart.com", "captchart.com"), 50);
Services.cookiemgr.add("captchart.com", "", "test200", "eviction",
false, false, false, futureExpiry, {});
do_check_eq(countCookies("captchart.com", "captchart.com"), 50);
enumerator = Services.cookiemgr.getCookiesFromHost("captchart.com", {});
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
do_check_true(cookie.expiry == futureExpiry);
}
do_finish_generator_test(test_generator);
}
// set 'aNumber' cookies with host 'aHost', with distinct names.
function
setCookies(aHost, aNumber, aExpiry)
{
for (let i = 0; i < aNumber; ++i)
Services.cookiemgr.add(aHost, "", "test" + i, "eviction",
false, false, false, aExpiry, {});
}
// count how many cookies are within domain 'aBaseDomain', using three
// independent interface methods on nsICookieManager2:
// 1) 'enumerator', an enumerator of all cookies;
// 2) 'countCookiesFromHost', which returns the number of cookies within the
// base domain of 'aHost',
// 3) 'getCookiesFromHost', which returns an enumerator of 2).
function
countCookies(aBaseDomain, aHost)
{
let enumerator = Services.cookiemgr.enumerator;
// count how many cookies are within domain 'aBaseDomain' using the cookie
// enumerator.
let cookies = [];
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.host.length >= aBaseDomain.length &&
cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain)
cookies.push(cookie);
}
// confirm the count using countCookiesFromHost and getCookiesFromHost.
let result = cookies.length;
do_check_eq(Services.cookiemgr.countCookiesFromHost(aBaseDomain),
cookies.length);
do_check_eq(Services.cookiemgr.countCookiesFromHost(aHost), cookies.length);
enumerator = Services.cookiemgr.getCookiesFromHost(aHost, {});
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.host.length >= aBaseDomain.length &&
cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain) {
let found = false;
for (let i = 0; i < cookies.length; ++i) {
if (cookies[i].host == cookie.host && cookies[i].name == cookie.name) {
found = true;
cookies.splice(i, 1);
break;
}
}
if (!found)
do_throw("cookie " + cookie.name + " not found in master enumerator");
} else {
do_throw("cookie host " + cookie.host + " not within domain " + aBaseDomain);
}
}
do_check_eq(cookies.length, 0);
return result;
}

View file

@ -0,0 +1,249 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var test_generator = do_run_test();
function run_test()
{
do_test_pending();
do_run_generator(test_generator);
}
function continue_test()
{
do_run_generator(test_generator);
}
function repeat_test()
{
// The test is probably going to fail because setting a batch of cookies took
// a significant fraction of 'gPurgeAge'. Compensate by rerunning the
// test with a larger purge age.
do_check_true(gPurgeAge < 64);
gPurgeAge *= 2;
gShortExpiry *= 2;
do_execute_soon(function() {
test_generator.close();
test_generator = do_run_test();
do_run_generator(test_generator);
});
}
// Purge threshold, in seconds.
var gPurgeAge = 1;
// Short expiry age, in seconds.
var gShortExpiry = 2;
// Required delay to ensure a purge occurs, in milliseconds. This must be at
// least gPurgeAge + 10%, and includes a little fuzz to account for timer
// resolution and possible differences between PR_Now() and Date.now().
function get_purge_delay()
{
return gPurgeAge * 1100 + 100;
}
// Required delay to ensure a cookie set with an expiry time 'gShortExpiry' into
// the future will have expired.
function get_expiry_delay()
{
return gShortExpiry * 1000 + 100;
}
function do_run_test()
{
// Set up a profile.
let profile = do_get_profile();
// twiddle prefs to convenient values for this test
Services.prefs.setIntPref("network.cookie.purgeAge", gPurgeAge);
Services.prefs.setIntPref("network.cookie.maxNumber", 100);
let expiry = Date.now() / 1000 + 1000;
// eviction is performed based on two limits: when the total number of cookies
// exceeds maxNumber + 10% (110), and when cookies are older than purgeAge
// (1 second). purging is done when both conditions are satisfied, and only
// those cookies are purged.
// we test the following cases of eviction:
// 1) excess and age are satisfied, but only some of the excess are old enough
// to be purged.
Services.cookiemgr.removeAll();
if (!set_cookies(0, 5, expiry)) {
repeat_test();
return;
}
// Sleep a while, to make sure the first batch of cookies is older than
// the second (timer resolution varies on different platforms).
do_timeout(get_purge_delay(), continue_test);
yield;
if (!set_cookies(5, 111, expiry)) {
repeat_test();
return;
}
// Fake a profile change, to ensure eviction affects the database correctly.
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(111, 5, 106));
// 2) excess and age are satisfied, and all of the excess are old enough
// to be purged.
Services.cookiemgr.removeAll();
if (!set_cookies(0, 10, expiry)) {
repeat_test();
return;
}
do_timeout(get_purge_delay(), continue_test);
yield;
if (!set_cookies(10, 111, expiry)) {
repeat_test();
return;
}
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(111, 10, 101));
// 3) excess and age are satisfied, and more than the excess are old enough
// to be purged.
Services.cookiemgr.removeAll();
if (!set_cookies(0, 50, expiry)) {
repeat_test();
return;
}
do_timeout(get_purge_delay(), continue_test);
yield;
if (!set_cookies(50, 111, expiry)) {
repeat_test();
return;
}
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(111, 50, 101));
// 4) excess but not age are satisfied.
Services.cookiemgr.removeAll();
if (!set_cookies(0, 120, expiry)) {
repeat_test();
return;
}
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(120, 0, 120));
// 5) age but not excess are satisfied.
Services.cookiemgr.removeAll();
if (!set_cookies(0, 20, expiry)) {
repeat_test();
return;
}
do_timeout(get_purge_delay(), continue_test);
yield;
if (!set_cookies(20, 110, expiry)) {
repeat_test();
return;
}
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(110, 20, 110));
// 6) Excess and age are satisfied, but the cookie limit can be satisfied by
// purging expired cookies.
Services.cookiemgr.removeAll();
let shortExpiry = Math.floor(Date.now() / 1000) + gShortExpiry;
if (!set_cookies(0, 20, shortExpiry)) {
repeat_test();
return;
}
do_timeout(get_expiry_delay(), continue_test);
yield;
if (!set_cookies(20, 110, expiry)) {
repeat_test();
return;
}
do_timeout(get_purge_delay(), continue_test);
yield;
if (!set_cookies(110, 111, expiry)) {
repeat_test();
return;
}
do_close_profile(test_generator);
yield;
do_load_profile();
do_check_true(check_remaining_cookies(111, 20, 91));
do_finish_generator_test(test_generator);
}
// Set 'end - begin' total cookies, with consecutively increasing hosts numbered
// 'begin' to 'end'.
function set_cookies(begin, end, expiry)
{
do_check_true(begin != end);
let beginTime;
for (let i = begin; i < end; ++i) {
let host = "eviction." + i + ".tests";
Services.cookiemgr.add(host, "", "test", "eviction", false, false, false,
expiry, {});
if (i == begin)
beginTime = get_creationTime(i);
}
let endTime = get_creationTime(end - 1);
do_check_true(begin == end - 1 || endTime > beginTime);
if (endTime - beginTime > gPurgeAge * 1000000) {
// Setting cookies took an amount of time very close to the purge threshold.
// Retry the test with a larger threshold.
return false;
}
return true;
}
function get_creationTime(i)
{
let host = "eviction." + i + ".tests";
let enumerator = Services.cookiemgr.getCookiesFromHost(host, {});
do_check_true(enumerator.hasMoreElements());
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
return cookie.creationTime;
}
// Test that 'aNumberToExpect' cookies remain after purging is complete, and
// that the cookies that remain consist of the set expected given the number of
// of older and newer cookies -- eviction should occur by order of lastAccessed
// time, if both the limit on total cookies (maxNumber + 10%) and the purge age
// + 10% are exceeded.
function check_remaining_cookies(aNumberTotal, aNumberOld, aNumberToExpect) {
var enumerator = Services.cookiemgr.enumerator;
let i = 0;
while (enumerator.hasMoreElements()) {
var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
++i;
if (aNumberTotal != aNumberToExpect) {
// make sure the cookie is one of the batch we expect was purged.
var hostNumber = new Number(cookie.rawHost.split(".")[1]);
if (hostNumber < (aNumberOld - aNumberToExpect)) break;
}
}
return i == aNumberToExpect;
}

View file

@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var pm;
// Create a principal based on the { origin, originAttributes }.
function createPrincipal(aOrigin, aOriginAttributes)
{
return Services.scriptSecurityManager.createCodebasePrincipal(NetUtil.newURI(aOrigin), aOriginAttributes);
}
// Return the data required by 'clear-origin-attributes-data' notification.
function getData(aPattern)
{
return JSON.stringify(aPattern);
}
// Use aEntries to create principals, add permissions to them and check that they have them.
// Then, it is notifying 'clear-origin-attributes-data' with the given aData and check if the permissions
// of principals[i] matches the permission in aResults[i].
function test(aEntries, aData, aResults)
{
let principals = [];
for (entry of aEntries) {
principals.push(createPrincipal(entry.origin, entry.originAttributes));
}
for (principal of principals) {
do_check_eq(pm.testPermissionFromPrincipal(principal, "test/clear-origin"), pm.UNKNOWN_ACTION);
pm.addFromPrincipal(principal, "test/clear-origin", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0);
do_check_eq(pm.testPermissionFromPrincipal(principal, "test/clear-origin"), pm.ALLOW_ACTION);
}
Services.obs.notifyObservers(null, 'clear-origin-attributes-data', aData);
var length = aEntries.length;
for (let i=0; i<length; ++i) {
do_check_eq(pm.testPermissionFromPrincipal(principals[i], 'test/clear-origin'), aResults[i]);
// Remove allowed actions.
if (aResults[i] == pm.ALLOW_ACTION) {
pm.removeFromPrincipal(principals[i], 'test/clear-origin');
}
}
}
function run_test()
{
do_get_profile();
pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
let entries = [
{ origin: 'http://example.com', originAttributes: { appId: 1 } },
{ origin: 'http://example.com', originAttributes: { appId: 1, inIsolatedMozBrowser: true } },
{ origin: 'http://example.com', originAttributes: {} },
{ origin: 'http://example.com', originAttributes: { appId: 2 } },
];
// In that case, all permissions from app 1 should be removed but not the other ones.
test(entries, getData({appId: 1}), [ pm.UNKNOWN_ACTION, pm.UNKNOWN_ACTION, pm.ALLOW_ACTION, pm.ALLOW_ACTION ]);
// In that case, only the permissions of app 1 related to a browserElement should be removed.
// All the other permissions should stay.
test(entries, getData({appId: 1, inIsolatedMozBrowser: true}), [ pm.ALLOW_ACTION, pm.UNKNOWN_ACTION, pm.ALLOW_ACTION, pm.ALLOW_ACTION ]);
}

View file

@ -0,0 +1,295 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// The origin we use in most of the tests.
const TEST_ORIGIN = NetUtil.newURI("http://example.org");
const TEST_ORIGIN_HTTPS = NetUtil.newURI("https://example.org");
const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com");
const TEST_ORIGIN_3 = NetUtil.newURI("https://example2.com:8080");
const TEST_PERMISSION = "test-permission";
Components.utils.import("resource://gre/modules/Promise.jsm");
function promiseTimeout(delay) {
let deferred = Promise.defer();
do_timeout(delay, deferred.resolve);
return deferred.promise;
}
function run_test() {
run_next_test();
}
add_task(function* do_test() {
// setup a profile.
do_get_profile();
// create a file in the temp directory with the defaults.
let file = do_get_tempdir();
file.append("test_default_permissions");
// write our test data to it.
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, 0o666, 0);
let conv = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
conv.init(ostream, "UTF-8", 0, 0);
conv.writeString("# this is a comment\n");
conv.writeString("\n"); // a blank line!
conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n");
conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n");
conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n");
conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "^appId=1000&inBrowser=1\n");
ostream.close();
// Set the preference used by the permission manager so the file is read.
Services.prefs.setCharPref("permissions.manager.defaultsUrl", "file://" + file.path);
// initialize the permission manager service - it will read that default.
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
// test the default permission was applied.
let principal = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, {});
let principalHttps = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_HTTPS, {});
let principal2 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_2, {});
let principal3 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_3, {});
let attrs = {appId: 1000, inIsolatedMozBrowser: true};
let principal4 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
let principal5 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_3, attrs);
attrs = {userContextId: 1};
let principal6 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
attrs = {firstPartyDomain: "cnn.com"};
let principal7 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
attrs = {userContextId: 1, firstPartyDomain: "cnn.com"};
let principal8 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principalHttps, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
// Didn't add
do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
pm.testPermissionFromPrincipal(principal5, TEST_PERMISSION));
// the permission should exist in the enumerator.
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN_3));
// but should not have been written to the DB
yield checkCapabilityViaDB(null);
// remove all should not throw and the default should remain
pm.removeAll();
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
// make sure principals with userContextId or firstPartyDomain use the same permissions
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
// Asking for this permission to be removed should result in that permission
// having UNKNOWN_ACTION
pm.removeFromPrincipal(principal, TEST_PERMISSION);
do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// make sure principals with userContextId or firstPartyDomain use the same permissions
do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
// and we should have this UNKNOWN_ACTION reflected in the DB
yield checkCapabilityViaDB(Ci.nsIPermissionManager.UNKNOWN_ACTION);
// but the permission should *not* appear in the enumerator.
do_check_eq(null, findCapabilityViaEnum());
// and a subsequent RemoveAll should restore the default
pm.removeAll();
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// make sure principals with userContextId or firstPartyDomain use the same permissions
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
// and allow it to again be seen in the enumerator.
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum());
// now explicitly add a permission - this too should override the default.
pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
// it should be reflected in a permission check, in the enumerator and the DB
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// make sure principals with userContextId or firstPartyDomain use the same permissions
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, findCapabilityViaEnum());
yield checkCapabilityViaDB(Ci.nsIPermissionManager.DENY_ACTION);
// explicitly add a different permission - in this case we are no longer
// replacing the default, but instead replacing the replacement!
pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.PROMPT_ACTION);
// it should be reflected in a permission check, in the enumerator and the DB
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// make sure principals with userContextId or firstPartyDomain use the same permissions
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum());
yield checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION);
// --------------------------------------------------------------
// check default permissions and removeAllSince work as expected.
pm.removeAll(); // ensure only defaults are there.
// default for both principals is allow.
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
// Add a default override for TEST_ORIGIN_2 - this one should *not* be
// restored in removeAllSince()
pm.addFromPrincipal(principal2, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
yield promiseTimeout(20);
let since = Number(Date.now());
yield promiseTimeout(20);
// explicitly add a permission which overrides the default for the first
// principal - this one *should* be removed by removeAllSince.
pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// do a removeAllSince.
pm.removeAllSince(since);
// the default for the first principal should re-appear as we modified it
// later then |since|
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
// but the permission for principal2 should remain as we added that before |since|.
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
// remove the temp file we created.
file.remove(false);
});
// use an enumerator to find the requested permission. Returns the permission
// value (ie, the "capability" in nsIPermission parlance) or null if it can't
// be found.
function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
let result = undefined;
let e = Services.perms.enumerator;
while (e.hasMoreElements()) {
let perm = e.getNext().QueryInterface(Ci.nsIPermission);
if (perm.matchesURI(origin, true) &&
perm.type == type) {
if (result !== undefined) {
// we've already found one previously - that's bad!
do_throw("enumerator found multiple entries");
}
result = perm.capability;
}
}
return result || null;
}
// A function to check the DB has the specified capability. As the permission
// manager uses async DB operations without a completion callback, the
// distinct possibility exists that our checking of the DB will happen before
// the permission manager update has completed - so we just retry a few times.
// Returns a promise.
function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMISSION) {
let deferred = Promise.defer();
let count = 0;
let max = 20;
let do_check = () => {
let got = findCapabilityViaDB(origin, type);
if (got == expected) {
// the do_check_eq() below will succeed - which is what we want.
do_check_eq(got, expected, "The database has the expected value");
deferred.resolve();
return;
}
// value isn't correct - see if we've retried enough
if (count++ == max) {
// the do_check_eq() below will fail - which is what we want.
do_check_eq(got, expected, "The database wasn't updated with the expected value");
deferred.resolve();
return;
}
// we can retry...
do_timeout(100, do_check);
}
do_check();
return deferred.promise;
}
// use the DB to find the requested permission. Returns the permission
// value (ie, the "capability" in nsIPermission parlance) or null if it can't
// be found.
function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
let principal = Services.scriptSecurityManager.createCodebasePrincipal(origin, {});
let originStr = principal.origin;
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append("permissions.sqlite");
let storage = Cc["@mozilla.org/storage/service;1"]
.getService(Ci.mozIStorageService);
let connection = storage.openDatabase(file);
let query = connection.createStatement(
"SELECT permission FROM moz_perms WHERE origin = :origin AND type = :type");
query.bindByName("origin", originStr);
query.bindByName("type", type);
if (!query.executeStep()) {
// no row
return null;
}
let result = query.getInt32(0);
if (query.executeStep()) {
// this is bad - we never expect more than 1 row here.
do_throw("More than 1 row found!")
}
return result;
}

View file

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that permissions with specific expiry times behave as expected.
var test_generator = do_run_test();
function run_test() {
do_test_pending();
test_generator.next();
}
function continue_test()
{
do_run_generator(test_generator);
}
function do_run_test() {
// Set up a profile.
let profile = do_get_profile();
let pm = Services.perms;
let permURI = NetUtil.newURI("http://example.com");
let principal = Services.scriptSecurityManager.createCodebasePrincipal(permURI, {});
let now = Number(Date.now());
// add a permission with *now* expiration
pm.addFromPrincipal(principal, "test/expiration-perm-exp", 1, pm.EXPIRE_TIME, now);
pm.addFromPrincipal(principal, "test/expiration-session-exp", 1, pm.EXPIRE_SESSION, now);
// add a permission with future expiration (100 milliseconds)
pm.addFromPrincipal(principal, "test/expiration-perm-exp2", 1, pm.EXPIRE_TIME, now + 100);
pm.addFromPrincipal(principal, "test/expiration-session-exp2", 1, pm.EXPIRE_SESSION, now + 100);
// add a permission with future expiration (1000 seconds)
pm.addFromPrincipal(principal, "test/expiration-perm-exp3", 1, pm.EXPIRE_TIME, now + 1e6);
pm.addFromPrincipal(principal, "test/expiration-session-exp3", 1, pm.EXPIRE_SESSION, now + 1e6);
// add a permission without expiration
pm.addFromPrincipal(principal, "test/expiration-perm-nexp", 1, pm.EXPIRE_NEVER, 0);
// add a permission for renewal
pm.addFromPrincipal(principal, "test/expiration-perm-renewable", 1, pm.EXPIRE_TIME, now + 100);
pm.addFromPrincipal(principal, "test/expiration-session-renewable", 1, pm.EXPIRE_SESSION, now + 100);
// And immediately renew them with longer timeouts
pm.updateExpireTime(principal, "test/expiration-perm-renewable", true, now + 100, now + 1e6);
pm.updateExpireTime(principal, "test/expiration-session-renewable", true, now + 1e6, now + 100);
// check that the second two haven't expired yet
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp3"));
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp3"));
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-nexp"));
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-renewable"));
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-renewable"));
// ... and the first one has
do_timeout(10, continue_test);
yield;
do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp"));
do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp"));
// ... and that the short-term one will
do_timeout(200, continue_test);
yield;
do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp2"));
do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp2"));
// Check that .getPermission returns a matching result
do_check_null(pm.getPermissionObject(principal, "test/expiration-perm-exp", false));
do_check_null(pm.getPermissionObject(principal, "test/expiration-session-exp", false));
do_check_null(pm.getPermissionObject(principal, "test/expiration-perm-exp2", false));
do_check_null(pm.getPermissionObject(principal, "test/expiration-session-exp2", false));
// Check that the renewable permissions actually got renewed
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-renewable"));
do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-renewable"));
do_finish_generator_test(test_generator);
}

View file

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function check_enumerator(uri, permissions) {
let pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
let enumerator = pm.getAllForURI(uri);
for ([type, capability] of permissions) {
let perm = enumerator.getNext();
do_check_true(perm != null);
do_check_true(perm.principal.URI.equals(uri));
do_check_eq(perm.type, type);
do_check_eq(perm.capability, capability);
do_check_eq(perm.expireType, pm.EXPIRE_NEVER);
}
do_check_false(enumerator.hasMoreElements());
}
function run_test() {
let pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
let uri = NetUtil.newURI("http://example.com");
let sub = NetUtil.newURI("http://sub.example.com");
check_enumerator(uri, [ ]);
pm.add(uri, "test/getallforuri", pm.ALLOW_ACTION);
check_enumerator(uri, [
[ "test/getallforuri", pm.ALLOW_ACTION ]
]);
// check that uris are matched exactly
check_enumerator(sub, [ ]);
pm.add(sub, "test/getallforuri", pm.PROMPT_ACTION);
pm.add(sub, "test/getallforuri2", pm.DENY_ACTION);
check_enumerator(sub, [
[ "test/getallforuri", pm.PROMPT_ACTION ],
[ "test/getallforuri2", pm.DENY_ACTION ]
]);
// check that the original uri list has not changed
check_enumerator(uri, [
[ "test/getallforuri", pm.ALLOW_ACTION ]
]);
// check that UNKNOWN_ACTION permissions are ignored
pm.add(uri, "test/getallforuri2", pm.UNKNOWN_ACTION);
pm.add(uri, "test/getallforuri3", pm.DENY_ACTION);
check_enumerator(uri, [
[ "test/getallforuri", pm.ALLOW_ACTION ],
[ "test/getallforuri3", pm.DENY_ACTION ]
]);
// check that permission updates are reflected
pm.add(uri, "test/getallforuri", pm.PROMPT_ACTION);
check_enumerator(uri, [
[ "test/getallforuri", pm.PROMPT_ACTION ],
[ "test/getallforuri3", pm.DENY_ACTION ]
]);
// check that permission removals are reflected
pm.remove(uri, "test/getallforuri");
check_enumerator(uri, [
[ "test/getallforuri3", pm.DENY_ACTION ]
]);
pm.removeAll();
check_enumerator(uri, [ ]);
check_enumerator(sub, [ ]);
}

View file

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function getPrincipalFromURI(aURI) {
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let uri = NetUtil.newURI(aURI);
return ssm.createCodebasePrincipal(uri, {});
}
function getSystemPrincipal() {
return Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getSystemPrincipal();
}
function run_test() {
var pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
do_check_null(pm.getPermissionObject(getSystemPrincipal(), "test/pobject", false));
let principal = getPrincipalFromURI("http://example.com");
let subPrincipal = getPrincipalFromURI("http://sub.example.com");
let subSubPrincipal = getPrincipalFromURI("http://sub.sub.example.com");
do_check_null(pm.getPermissionObject(principal, "test/pobject", false));
do_check_null(pm.getPermissionObject(principal, "test/pobject", true));
pm.addFromPrincipal(principal, "test/pobject", pm.ALLOW_ACTION);
var rootPerm = pm.getPermissionObject(principal, "test/pobject", false);
do_check_true(rootPerm != null);
do_check_eq(rootPerm.principal.origin, "http://example.com");
do_check_eq(rootPerm.type, "test/pobject");
do_check_eq(rootPerm.capability, pm.ALLOW_ACTION);
do_check_eq(rootPerm.expireType, pm.EXPIRE_NEVER);
var rootPerm2 = pm.getPermissionObject(principal, "test/pobject", true);
do_check_true(rootPerm != null);
do_check_eq(rootPerm.principal.origin, "http://example.com");
var subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
do_check_null(subPerm);
subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
do_check_true(subPerm != null);
do_check_eq(subPerm.principal.origin, "http://example.com");
do_check_eq(subPerm.type, "test/pobject");
do_check_eq(subPerm.capability, pm.ALLOW_ACTION);
subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true);
do_check_null(subPerm);
subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
do_check_true(subPerm != null);
do_check_eq(subPerm.principal.origin, "http://example.com");
pm.addFromPrincipal(principal, "test/pobject", pm.DENY_ACTION, pm.EXPIRE_SESSION);
// make sure permission objects are not dynamic
do_check_eq(rootPerm.capability, pm.ALLOW_ACTION);
// but do update on change
rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
do_check_eq(rootPerm.capability, pm.DENY_ACTION);
do_check_eq(rootPerm.expireType, pm.EXPIRE_SESSION);
subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
do_check_eq(subPerm.principal.origin, "http://example.com");
do_check_eq(subPerm.capability, pm.DENY_ACTION);
do_check_eq(subPerm.expireType, pm.EXPIRE_SESSION);
pm.addFromPrincipal(subPrincipal, "test/pobject", pm.PROMPT_ACTION);
rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
do_check_eq(rootPerm.principal.origin, "http://example.com");
do_check_eq(rootPerm.capability, pm.DENY_ACTION);
subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
do_check_eq(subPerm.principal.origin, "http://sub.example.com");
do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
do_check_eq(subPerm.principal.origin, "http://sub.example.com");
do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true);
do_check_null(subPerm);
subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
do_check_eq(subPerm.principal.origin, "http://sub.example.com");
do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
pm.removeFromPrincipal(principal, "test/pobject");
rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
do_check_null(rootPerm);
}

View file

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function getPrincipalFromDomain(aDomain) {
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let uri = NetUtil.newURI("http://" + aDomain);
return ssm.createCodebasePrincipal(uri, {});
}
function run_test() {
let profile = do_get_profile();
let pm = Services.perms;
let perm = 'test-idn';
// We create three principal linked to IDN.
// One with just a domain, one with a subdomain and one with the TLD
// containing a UTF-8 character.
let mainDomainPrincipal = getPrincipalFromDomain("fôû.com");
let subDomainPrincipal = getPrincipalFromDomain("fôô.bàr.com");
let tldPrincipal = getPrincipalFromDomain("fôû.bàr.côm");
// We add those to the permission manager.
pm.addFromPrincipal(mainDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
pm.addFromPrincipal(subDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
pm.addFromPrincipal(tldPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
// They should obviously be there now..
do_check_eq(pm.testPermissionFromPrincipal(mainDomainPrincipal, perm), pm.ALLOW_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(subDomainPrincipal, perm), pm.ALLOW_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(tldPrincipal, perm), pm.ALLOW_ACTION);
// We do the same thing with the puny-encoded versions of the IDN.
let punyMainDomainPrincipal = getPrincipalFromDomain('xn--f-xgav.com');
let punySubDomainPrincipal = getPrincipalFromDomain('xn--f-xgaa.xn--br-jia.com');
let punyTldPrincipal = getPrincipalFromDomain('xn--f-xgav.xn--br-jia.xn--cm-8ja');
// Those principals should have the permission granted too.
do_check_eq(pm.testPermissionFromPrincipal(punyMainDomainPrincipal, perm), pm.ALLOW_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(punySubDomainPrincipal, perm), pm.ALLOW_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(punyTldPrincipal, perm), pm.ALLOW_ACTION);
// However, those two principals shouldn't be allowed because they are like
// the IDN but without the UT8-8 characters.
let witnessPrincipal = getPrincipalFromDomain("foo.com");
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, perm), pm.UNKNOWN_ACTION);
witnessPrincipal = getPrincipalFromDomain("foo.bar.com");
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, perm), pm.UNKNOWN_ACTION);
}

View file

@ -0,0 +1,142 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var DEBUG_TEST = false;
function run_test() {
// Setup a profile directory.
var dir = do_get_profile();
// Get the db file.
var file = dir.clone();
file.append("permissions.sqlite");
var storage = Cc["@mozilla.org/storage/service;1"]
.getService(Ci.mozIStorageService);
// Create database.
var connection = storage.openDatabase(file);
// The file should now exist.
do_check_true(file.exists());
connection.schemaVersion = 3;
connection.executeSimpleSQL(
"CREATE TABLE moz_hosts (" +
" id INTEGER PRIMARY KEY" +
",host TEXT" +
",type TEXT" +
",permission INTEGER" +
",expireType INTEGER" +
",expireTime INTEGER" +
",appId INTEGER" +
",isInBrowserElement INTEGER" +
")");
// Now we can inject garbadge in the database.
var garbadge = [
// Regular entry.
{ host: '42', type: '0', permission: 1, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
// Special values in host (some being invalid).
{ host: 'scheme:file', type: '1', permission: 0, expireType: 0,
expireTime: 0, appId: 0, isInBrowserElement: 0 },
{ host: '192.168.0.1', type: '2', permission: 0, expireType: 0,
expireTime: 0, appId: 0, isInBrowserElement: 0 },
{ host: '2001:0db8:0000:0000:0000:ff00:0042:8329', type: '3', permission: 0,
expireType: 0, expireTime: 0, appId: 0, isInBrowserElement: 0 },
{ host: '::1', type: '4', permission: 0, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
// Permission is UNKNOWN_ACTION.
{ host: '42', type: '5', permission: Ci.nsIPermissionManager.UNKNOWN_ACTION,
expireType: 0, expireTime: 0, appId: 0, isInBrowserElement: 0 },
// Permission is out of range.
{ host: '42', type: '6', permission: 100, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
{ host: '42', type: '7', permission: -100, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
// ExpireType is out of range.
{ host: '42', type: '8', permission: 1, expireType: -100, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
{ host: '42', type: '9', permission: 1, expireType: 100, expireTime: 0,
appId: 0, isInBrowserElement: 0 },
// ExpireTime is at 0 with ExpireType = Time.
{ host: '42', type: '10', permission: 1,
expireType: Ci.nsIPermissionManager.EXPIRE_TIME, expireTime: 0, appId: 0,
isInBrowserElement: 0 },
// ExpireTime has a value with ExpireType != Time
{ host: '42', type: '11', permission: 1,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, expireTime: 1000,
appId: 0, isInBrowserElement: 0 },
{ host: '42', type: '12', permission: 1,
expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, expireTime: 1000,
appId: 0, isInBrowserElement: 0 },
// ExpireTime is negative.
{ host: '42', type: '13', permission: 1,
expireType: Ci.nsIPermissionManager.EXPIRE_TIME, expireTime: -1,
appId: 0, isInBrowserElement: 0 },
// AppId is negative.
{ host: '42', type: '14', permission: 1, expireType: 0, expireTime: 0,
appId: -1, isInBrowserElement: 0 },
// IsInBrowserElement is negative or higher than 1.
{ host: '42', type: '15', permission: 1, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: -1 },
{ host: '42', type: '16', permission: 1, expireType: 0, expireTime: 0,
appId: 0, isInBrowserElement: 10 },
// This insertion should be the last one. It is used to make sure we always
// load it regardless of the previous entries validities.
{ host: 'example.org', type: 'test-load-invalid-entries',
permission: Ci.nsIPermissionManager.ALLOW_ACTION, expireType: 0,
expireTime: 0, appId: 0, isInBrowserElement: 0 },
];
for (var i=0; i<garbadge.length; ++i) {
if (DEBUG_TEST) {
dump("\n value #" + i + "\n\n");
}
var data = garbadge[i];
connection.executeSimpleSQL(
"INSERT INTO moz_hosts " +
" (id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " +
"VALUES (" + i + ", '" + data.host + "', '" + data.type + "', "
+ data.permission + ", " + data.expireType + ", "
+ data.expireTime + ", " + data.appId + ", "
+ data.isInBrowserElement + ")"
);
}
let earliestNow = Number(Date.now());
// Initialize the permission manager service
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
let latestNow = Number(Date.now());
// The schema should be upgraded to 9, and a 'modificationTime' column should
// exist with all records having a value of 0.
do_check_eq(connection.schemaVersion, 9);
let select = connection.createStatement("SELECT modificationTime FROM moz_perms")
let numMigrated = 0;
while (select.executeStep()) {
let thisModTime = select.getInt64(0);
do_check_true(thisModTime == 0, "new modifiedTime field is correct");
numMigrated += 1;
}
// check we found at least 1 record that was migrated.
do_check_true(numMigrated > 0, "we found at least 1 record that was migrated");
// This permission should always be there.
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let uri = NetUtil.newURI("http://example.org");
let principal = ssm.createCodebasePrincipal(uri, {});
do_check_eq(pm.testPermissionFromPrincipal(principal, 'test-load-invalid-entries'), Ci.nsIPermissionManager.ALLOW_ACTION);
}

View file

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that permissions work for file:// URIs (aka local files).
function getPrincipalFromURIString(uriStr)
{
let uri = NetUtil.newURI(uriStr);
return Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
}
function run_test() {
let pm = Services.perms;
// If we add a permission to a file:// URI, the test should return true.
let principal = getPrincipalFromURIString("file:///foo/bar");
pm.addFromPrincipal(principal, "test/local-files", pm.ALLOW_ACTION, 0, 0);
do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.ALLOW_ACTION);
// Another file:// URI should have the same permission.
let witnessPrincipal = getPrincipalFromURIString("file:///bar/foo");
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
// Giving "file:///" a permission shouldn't give it to all file:// URIs.
let rootPrincipal = getPrincipalFromURIString("file:///");
pm.addFromPrincipal(rootPrincipal, "test/local-files", pm.ALLOW_ACTION, 0, 0);
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
// Giving "file://" a permission shouldn't give it to all file:// URIs.
let schemeRootPrincipal = getPrincipalFromURIString("file://");
pm.addFromPrincipal(schemeRootPrincipal, "test/local-files", pm.ALLOW_ACTION, 0, 0);
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
// Giving 'node' a permission shouldn't give it to its 'children'.
let fileInDirPrincipal = getPrincipalFromURIString("file:///foo/bar/foobar.txt");
do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
// Revert "file:///foo/bar" permission and check that it has been correctly taken into account.
pm.removeFromPrincipal(principal, "test/local-files");
do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.UNKNOWN_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
}

View file

@ -0,0 +1,183 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function matches_always(perm, principals) {
principals.forEach((principal) => {
do_check_true(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
});
}
function matches_weak(perm, principals) {
principals.forEach((principal) => {
do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
});
}
function matches_never(perm, principals) {
principals.forEach((principal) => {
do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
do_check_false(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
});
}
function run_test() {
// initialize the permission manager service
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
// Add some permissions
let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag", null, null);
let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir", null, null);
let uri2 = NetUtil.newURI("http://google.org/", null, null);
let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory", null, null);
let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout", null, null);
let uri5 = NetUtil.newURI("http://google.com:8096/", null, null);
let uri0_n_n = secMan.createCodebasePrincipal(uri0, {});
let uri1_n_n = secMan.createCodebasePrincipal(uri1, {});
let uri2_n_n = secMan.createCodebasePrincipal(uri2, {});
let uri3_n_n = secMan.createCodebasePrincipal(uri3, {});
let uri4_n_n = secMan.createCodebasePrincipal(uri4, {});
let uri5_n_n = secMan.createCodebasePrincipal(uri5, {});
let attrs = {appId: 1000};
let uri0_1000_n = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_1000_n = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_1000_n = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_1000_n = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_1000_n = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_1000_n = secMan.createCodebasePrincipal(uri5, attrs);
attrs = {appId: 1000, inIsolatedMozBrowser: true};
let uri0_1000_y = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_1000_y = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_1000_y = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_1000_y = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_1000_y = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_1000_y = secMan.createCodebasePrincipal(uri5, attrs);
attrs = {appId: 2000};
let uri0_2000_n = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_2000_n = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_2000_n = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_2000_n = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_2000_n = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_2000_n = secMan.createCodebasePrincipal(uri5, attrs);
attrs = {appId: 2000, inIsolatedMozBrowser: true};
let uri0_2000_y = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_2000_y = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_2000_y = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_2000_y = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_2000_y = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_2000_y = secMan.createCodebasePrincipal(uri5, attrs);
attrs = {userContextId: 1};
let uri0_1 = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_1 = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_1 = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_1 = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_1 = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_1 = secMan.createCodebasePrincipal(uri5, attrs);
attrs = {firstPartyDomain: "cnn.com"};
let uri0_cnn = secMan.createCodebasePrincipal(uri0, attrs);
let uri1_cnn = secMan.createCodebasePrincipal(uri1, attrs);
let uri2_cnn = secMan.createCodebasePrincipal(uri2, attrs);
let uri3_cnn = secMan.createCodebasePrincipal(uri3, attrs);
let uri4_cnn = secMan.createCodebasePrincipal(uri4, attrs);
let uri5_cnn = secMan.createCodebasePrincipal(uri5, attrs);
pm.addFromPrincipal(uri0_n_n, "test/matches", pm.ALLOW_ACTION);
let perm_n_n = pm.getPermissionObject(uri0_n_n, "test/matches", true);
pm.addFromPrincipal(uri0_1000_n, "test/matches", pm.ALLOW_ACTION);
let perm_1000_n = pm.getPermissionObject(uri0_1000_n, "test/matches", true);
pm.addFromPrincipal(uri0_1000_y, "test/matches", pm.ALLOW_ACTION);
let perm_1000_y = pm.getPermissionObject(uri0_1000_y, "test/matches", true);
pm.addFromPrincipal(uri0_2000_n, "test/matches", pm.ALLOW_ACTION);
let perm_2000_n = pm.getPermissionObject(uri0_2000_n, "test/matches", true);
pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION);
let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true);
pm.addFromPrincipal(uri0_1, "test/matches", pm.ALLOW_ACTION);
let perm_1 = pm.getPermissionObject(uri0_n_n, "test/matches", true);
pm.addFromPrincipal(uri0_cnn, "test/matches", pm.ALLOW_ACTION);
let perm_cnn = pm.getPermissionObject(uri0_n_n, "test/matches", true);
matches_always(perm_n_n, [uri0_n_n, uri0_1, uri0_cnn]);
matches_weak(perm_n_n, [uri1_n_n, uri1_1, uri1_cnn]);
matches_never(perm_n_n, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri2_1, uri3_1, uri4_1, uri5_1,
uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_1000_n, [uri0_1000_n]);
matches_weak(perm_1000_n, [uri1_1000_n]);
matches_never(perm_1000_n, [uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_1000_y, [uri0_1000_y]);
matches_weak(perm_1000_y, [uri1_1000_y]);
matches_never(perm_1000_y, [uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_2000_n, [uri0_2000_n]);
matches_weak(perm_2000_n, [uri1_2000_n]);
matches_never(perm_2000_n, [uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_2000_y, [uri0_2000_y]);
matches_weak(perm_2000_y, [uri1_2000_y]);
matches_never(perm_2000_y, [uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_1, [uri0_n_n, uri0_1, uri0_cnn]);
matches_weak(perm_1, [uri1_n_n, uri1_1, uri1_cnn]);
matches_never(perm_1, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri2_1, uri3_1, uri4_1, uri5_1,
uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
matches_always(perm_cnn, [uri0_n_n, uri0_1, uri0_cnn]);
matches_weak(perm_cnn, [uri1_n_n, uri1_1, uri1_cnn]);
matches_never(perm_cnn, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
uri2_1, uri3_1, uri4_1, uri5_1,
uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
// Clean up!
pm.removeAll();
}

View file

@ -0,0 +1,150 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function matches_always(perm, uris) {
uris.forEach((uri) => {
do_check_true(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
});
}
function matches_weak(perm, uris) {
uris.forEach((uri) => {
do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
});
}
function matches_never(perm, uris) {
uris.forEach((uri) => {
do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
do_check_false(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
});
}
function mk_permission(uri, isAppPermission = false) {
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
// Get the permission from the principal!
let attrs = {appId: 1000};
let principal =
secMan.createCodebasePrincipal(uri, isAppPermission ? attrs : {});
pm.addFromPrincipal(principal, "test/matchesuri", pm.ALLOW_ACTION);
let permission = pm.getPermissionObject(principal, "test/matchesuri", true);
return permission;
}
function run_test() {
// initialize the permission manager service
let pm = Cc["@mozilla.org/permissionmanager;1"].
getService(Ci.nsIPermissionManager);
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let fileprefix = "file:///";
if (Services.appinfo.OS == "WINNT") {
// Windows rejects files if they don't have a drive. See Bug 1180870
fileprefix += "c:/";
}
// Add some permissions
let uri0 = NetUtil.newURI("http://google.com:9091/just/a/path", null, null);
let uri1 = NetUtil.newURI("http://hangouts.google.com:9091/some/path", null, null);
let uri2 = NetUtil.newURI("http://google.com:9091/", null, null);
let uri3 = NetUtil.newURI("http://google.org:9091/", null, null);
let uri4 = NetUtil.newURI("http://deeper.hangouts.google.com:9091/", null, null);
let uri5 = NetUtil.newURI("https://google.com/just/a/path", null, null);
let uri6 = NetUtil.newURI("https://hangouts.google.com", null, null);
let uri7 = NetUtil.newURI("https://google.com/", null, null);
let fileuri1 = NetUtil.newURI(fileprefix + "a/file/path", null, null);
let fileuri2 = NetUtil.newURI(fileprefix + "a/file/path/deeper", null, null);
let fileuri3 = NetUtil.newURI(fileprefix + "a/file/otherpath", null, null);
{
let perm = mk_permission(uri0);
matches_always(perm, [uri0, uri2]);
matches_weak(perm, [uri1, uri4]);
matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri1);
matches_always(perm, [uri1]);
matches_weak(perm, [uri4]);
matches_never(perm, [uri0, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri2);
matches_always(perm, [uri0, uri2]);
matches_weak(perm, [uri1, uri4]);
matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri3);
matches_always(perm, [uri3]);
matches_weak(perm, []);
matches_never(perm, [uri1, uri2, uri4, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri4);
matches_always(perm, [uri4]);
matches_weak(perm, []);
matches_never(perm, [uri1, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri5);
matches_always(perm, [uri5, uri7]);
matches_weak(perm, [uri6]);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri6);
matches_always(perm, [uri6]);
matches_weak(perm, []);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri7, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(uri7);
matches_always(perm, [uri5, uri7]);
matches_weak(perm, [uri6]);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]);
}
{
let perm = mk_permission(fileuri1);
matches_always(perm, [fileuri1]);
matches_weak(perm, []);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri2, fileuri3]);
}
{
let perm = mk_permission(fileuri2);
matches_always(perm, [fileuri2]);
matches_weak(perm, []);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri3]);
}
{
let perm = mk_permission(fileuri3);
matches_always(perm, [fileuri3]);
matches_weak(perm, []);
matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri2]);
}
// Clean up!
pm.removeAll();
}

View file

@ -0,0 +1,207 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
var PERMISSIONS_FILE_NAME = "permissions.sqlite";
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
function run_test() {
run_next_test();
}
add_task(function test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 4;
db.executeSimpleSQL(
"CREATE TABLE moz_hosts (" +
" id INTEGER PRIMARY KEY" +
",host TEXT" +
",type TEXT" +
",permission INTEGER" +
",expireType INTEGER" +
",expireTime INTEGER" +
",modificationTime INTEGER" +
",appId INTEGER" +
",isInBrowserElement INTEGER" +
")");
let stmtInsert = db.createStatement(
"INSERT INTO moz_hosts (" +
"id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
") VALUES (" +
":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
")");
let id = 0;
function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) {
let thisId = id++;
stmtInsert.bindByName("id", thisId);
stmtInsert.bindByName("host", host);
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
stmtInsert.execute();
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
modificationTime: modificationTime,
appId: appId,
isInBrowserElement: isInBrowserElement
};
}
// Add some rows to the database
let created = [
insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false),
insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
];
// CLose the db connection
stmtInsert.finalize();
db.close();
stmtInsert = null;
db = null;
let expected = [
// The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
// and http://foo.com or a subdomain are never visited.
// ["http://foo.com", "A", 1, 0, 0],
// ["http://foo.com^appId=1000", "A", 1, 0, 0],
// ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
//
// Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries
// for subdomains of foo.com either
// ["http://sub.foo.com", "B", 1, 0, 0],
// ["http://subber.sub.foo.com", "B", 1, 0, 0],
["https://foo.com", "A", 1, 0, 0],
["https://foo.com", "C", 1, 0, 0],
["https://foo.com^appId=1000", "A", 1, 0, 0],
["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
["https://sub.foo.com", "B", 1, 0, 0],
["https://subber.sub.foo.com", "B", 1, 0, 0],
// bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
["http://bar.ca", "B", 1, 0, 0],
["https://bar.ca", "B", 1, 0, 0],
["http://bar.ca^appId=1000", "B", 1, 0, 0],
["https://bar.ca^appId=1000", "B", 1, 0, 0],
["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
["file:///some/path/to/file.html", "A", 1, 0, 0],
["file:///another/file.html", "A", 1, 0, 0],
// Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
// also have these entries
["ftp://foo.com:8000", "A", 1, 0, 0],
["ftp://foo.com:8000", "C", 1, 0, 0],
["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0],
["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0],
// In addition, because we search for port/scheme combinations under eTLD+1, we should have the
// following entries
["ftp://sub.foo.com:8000", "B", 1, 0, 0],
["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0],
// Make sure that we also support localhost, and IP addresses
["http://localhost", "A", 1, 0, 0],
["https://localhost", "A", 1, 0, 0],
["http://127.0.0.1", "A", 1, 0, 0],
["https://127.0.0.1", "A", 1, 0, 0],
["http://192.0.2.235", "A", 1, 0, 0],
["https://192.0.2.235", "A", 1, 0, 0],
];
let found = expected.map((it) => 0);
// Add some places to the places database
yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null));
yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null));
// Force initialization of the nsPermissionManager
let enumerator = Services.perms.enumerator;
while (enumerator.hasMoreElements()) {
let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
let isExpected = false;
expected.forEach((it, i) => {
if (permission.principal.origin == it[0] &&
permission.type == it[1] &&
permission.capability == it[2] &&
permission.expireType == it[3] &&
permission.expireTime == it[4]) {
isExpected = true;
found[i]++;
}
});
do_check_true(isExpected,
"Permission " + (isExpected ? "should" : "shouldn't") +
" be in permission database: " +
permission.principal.origin + ", " +
permission.type + ", " +
permission.capability + ", " +
permission.expireType + ", " +
permission.expireTime);
}
found.forEach((count, i) => {
do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]);
});
// Check to make sure that all of the tables which we care about are present
{
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
mozHostsCount.executeStep();
do_check_eq(mozHostsCount.getInt64(0), 0);
db.close();
}
});

View file

@ -0,0 +1,226 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var PERMISSIONS_FILE_NAME = "permissions.sqlite";
/*
* Prevent the nsINavHistoryService from being avaliable for the migration
*/
var CONTRACT_ID = "@mozilla.org/browser/nav-history-service;1";
var factory = {
createInstance: function() {
throw new Error("There is no history service");
},
lockFactory: function() {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
var oldClassID = registrar.contractIDToCID(CONTRACT_ID);
var oldFactory = Components.manager.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
registrar.unregisterFactory(oldClassID, oldFactory);
registrar.registerFactory(newClassID, "", CONTRACT_ID, factory);
function cleanupFactory() {
registrar.unregisterFactory(newClassID, factory);
registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
}
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
/*
* Done nsINavHistoryService code
*/
function run_test() {
run_next_test();
}
add_task(function test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
// Make sure that we can't resolve the nsINavHistoryService
try {
Cc['@mozilla.org/browser/nav-history-service;1'].getService(Ci.nsINavHistoryService);
do_check_true(false, "There shouldn't have been a nsINavHistoryService");
} catch (e) {
do_check_true(true, "There wasn't a nsINavHistoryService");
}
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 4;
db.executeSimpleSQL(
"CREATE TABLE moz_hosts (" +
" id INTEGER PRIMARY KEY" +
",host TEXT" +
",type TEXT" +
",permission INTEGER" +
",expireType INTEGER" +
",expireTime INTEGER" +
",modificationTime INTEGER" +
",appId INTEGER" +
",isInBrowserElement INTEGER" +
")");
let stmtInsert = db.createStatement(
"INSERT INTO moz_hosts (" +
"id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
") VALUES (" +
":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
")");
let id = 0;
function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) {
let thisId = id++;
stmtInsert.bindByName("id", thisId);
stmtInsert.bindByName("host", host);
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
stmtInsert.execute();
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
modificationTime: modificationTime,
appId: appId,
isInBrowserElement: isInBrowserElement
};
}
// Add some rows to the database
let created = [
insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
insertHost("263.123.555.676", "A", 1, 0, 0, 0, 0, false),
insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
];
// CLose the db connection
stmtInsert.finalize();
db.close();
stmtInsert = null;
db = null;
let expected = [
["http://foo.com", "A", 1, 0, 0],
["http://foo.com", "C", 1, 0, 0],
["http://foo.com^appId=1000", "A", 1, 0, 0],
["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
["http://sub.foo.com", "B", 1, 0, 0],
["http://subber.sub.foo.com", "B", 1, 0, 0],
["https://foo.com", "A", 1, 0, 0],
["https://foo.com", "C", 1, 0, 0],
["https://foo.com^appId=1000", "A", 1, 0, 0],
["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
["https://sub.foo.com", "B", 1, 0, 0],
["https://subber.sub.foo.com", "B", 1, 0, 0],
// bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
["http://bar.ca", "B", 1, 0, 0],
["https://bar.ca", "B", 1, 0, 0],
["http://bar.ca^appId=1000", "B", 1, 0, 0],
["https://bar.ca^appId=1000", "B", 1, 0, 0],
["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
["file:///some/path/to/file.html", "A", 1, 0, 0],
["file:///another/file.html", "A", 1, 0, 0],
// Make sure that we also support localhost, and IP addresses
["http://localhost", "A", 1, 0, 0],
["https://localhost", "A", 1, 0, 0],
["http://127.0.0.1", "A", 1, 0, 0],
["https://127.0.0.1", "A", 1, 0, 0],
["http://263.123.555.676", "A", 1, 0, 0],
["https://263.123.555.676", "A", 1, 0, 0],
];
let found = expected.map((it) => 0);
// Force initialization of the nsPermissionManager
let enumerator = Services.perms.enumerator;
while (enumerator.hasMoreElements()) {
let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
let isExpected = false;
expected.forEach((it, i) => {
if (permission.principal.origin == it[0] &&
permission.type == it[1] &&
permission.capability == it[2] &&
permission.expireType == it[3] &&
permission.expireTime == it[4]) {
isExpected = true;
found[i]++;
}
});
do_check_true(isExpected,
"Permission " + (isExpected ? "should" : "shouldn't") +
" be in permission database: " +
permission.principal.origin + ", " +
permission.type + ", " +
permission.capability + ", " +
permission.expireType + ", " +
permission.expireTime);
}
found.forEach((count, i) => {
do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]);
});
// Check to make sure that all of the tables which we care about are present
{
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
mozHostsCount.executeStep();
do_check_eq(mozHostsCount.getInt64(0), 0);
db.close();
}
cleanupFactory();
});

Some files were not shown because too many files have changed in this diff Show more